diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 00000000..6b3a5714 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,17 @@ +version = 1 + +[[analyzers]] +name = "csharp" +enabled = true + +[[analyzers]] +name = "test-coverage" +enabled = true + +[[analyzers]] +name = "secrets" +enabled = true + +[[transformers]] +name = "dotnet-format" +enabled = true \ No newline at end of file diff --git a/.github/TradingPlatform.BusinessLayer.dll b/.github/TradingPlatform.BusinessLayer.dll index 0a324bcb..d1cca1dc 100644 Binary files a/.github/TradingPlatform.BusinessLayer.dll and b/.github/TradingPlatform.BusinessLayer.dll differ diff --git a/.github/TradingPlatform.BusinessLayer.xml b/.github/TradingPlatform.BusinessLayer.xml index 47ebf0f0..60fa288b 100644 --- a/.github/TradingPlatform.BusinessLayer.xml +++ b/.github/TradingPlatform.BusinessLayer.xml @@ -377,21 +377,6 @@ Mediates a history meta data with available data types and intervals on vendor side - - - History data types - - - - - History intervals - - - - - History intervals - - Asset id bearer @@ -1948,7 +1933,7 @@ - + Gets historical data according to aggregation and other parameters @@ -2965,21 +2950,11 @@ Gets HistoricalData symbol - - - Gets HistoricalData Period - - Gets HistoricalData aggregation - - - Gets HistoricalData history type - - Gets HistoricalData left time boundary diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml new file mode 100644 index 00000000..44d7bdaf --- /dev/null +++ b/.github/workflows/Publish.yml @@ -0,0 +1,254 @@ +# This workflow integrates SonarCloud analysis, coverage reporting, +# CodeQL analysis, SecurityCodeScan, and Codacy Security Scan +# for code scanning and vulnerability detection - and if they all pass, publish + +name: Publish Workflow + +on: + push: # Triggers on push events to any branch + pull_request: # Triggers on pull request events targeting any branch + workflow_dispatch: # Allows manual triggering of the workflow + +permissions: + contents: write + pull-requests: read # Allows SonarCloud to decorate PRs with analysis results + security-events: write # Required for CodeQL analysis and uploading SARIF results + +jobs: + Code_Coverage: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.x' + + - name: Install dotnet tools + run: | + dotnet tool install JetBrains.dotCover.GlobalTool --global + dotnet tool install dotnet-sonarscanner --global + dotnet tool install dotnet-coverage --global + dotnet tool install --global coverlet.console + dotnet tool install --global dotnet-reportgenerator-globaltool + dotnet restore + + - name: Build Projects + run: | + dotnet build --no-restore --configuration Debug + dotnet build ./lib/quantalib.csproj --configuration Release --nologo + dotnet build ./quantower/Averages/_Averages.csproj --configuration Release --nologo + dotnet build ./quantower/Statistics/_Statistics.csproj --configuration Release --nologo + dotnet build ./quantower/Volatility/_Volatility.csproj --configuration Release --nologo + dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo + + - name: Run Tests with Coverage + run: | + dotnet test --no-build --configuration Debug /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml" + dotnet dotcover test Tests/Tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html + dotnet dotcover test Tests/Tests.csproj --dcReportType=DetailedXML --dcoutput=./dotcover.xml --verbosity=Detailed + dotnet test -p:CollectCoverage=true --collect:"XPlat Code Coverage" --results-directory "./" + + - name: Generate Coverage Report + run: | + reportgenerator -reports:*cover*.xml -targetdir:. + + - name: Upload Coverage to Codacy + uses: codacy/codacy-coverage-reporter-action@v1 + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: '*cover*.xml' + + - name: Upload Coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: 'cover*' + verbose: true + + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: > + -Dsonar.projectKey=mihakralj_QuanTAlib + -Dsonar.organization=mihakralj-quantalib + -Dsonar.sources=. + -Dsonar.cs.opencover.reportsPaths=**/*cover*.xml + -Dsonar.cs.dotcover.reportsPaths=**/dotcover.xml + -Dsonar.coverage.exclusions=**Tests.cs + + CodeQL: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.x' + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: 'csharp' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Debug + + - name: Run Codacy Analysis CLI + uses: codacy/codacy-analysis-cli-action@v4 + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + verbose: true + output: results.sarif + format: sarif + gh-code-scanning-compat: true + max-allowed-issues: 2147483647 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + + 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@v4 + 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 + + + build_publish: + needs: [Code_Coverage, CodeQL, SecurityCodeScan] + if: | + success() && + (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) || + github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.x' + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0 + with: + versionSpec: '6.x' + includePrerelease: true + + - name: Determine Version + id: gitversion + uses: gittools/actions/gitversion/execute@v0 + with: + useConfigFile: true + updateAssemblyInfo: true + + - name: Build projects + run: | + dotnet build ./lib/quantalib.csproj --configuration Release --nologo \ + -p:PackageVersion=${{ steps.gitversion.outputs.MajorMinorPatch }} + dotnet build ./quantower/Averages/_Averages.csproj --configuration Release --nologo + dotnet build ./quantower/Statistics/_Statistics.csproj --configuration Release --nologo + dotnet build ./quantower/Volatility/_Volatility.csproj --configuration Release --nologo + dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo + +############# Publish dev release + + - name: Create or Update Development Release + if: github.ref == 'refs/heads/dev' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release delete development --yes || true + gh release create development \ + --title "Development Build" \ + --notes "Latest development build from commit ${{ github.sha }}" \ + --prerelease \ + --target ${{ github.sha }} \ + lib/bin/Release/QuanTAlib.dll \ + quantower/Averages/bin/Release/Averages.dll \ + quantower/Statistics/bin/Release/Statistics.dll \ + quantower/Volatility/bin/Release/Volatility.dll \ + SyntheticVendor/bin/Release/SyntheticVendor.dll + + - name: Push prerelease package to myget.org + if: github.ref == 'refs/heads/dev' + run: | + dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \ + --source https://www.myget.org/F/quantalib/api/v3/index.json \ + --force-english-output \ + --api-key ${{ secrets.MYGET_DEPLOY_KEY_QUANTALIB }} + +############## Publish main release + + - name: Create GitHub Release + if: ${{ github.ref == 'refs/heads/main' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create v${{ steps.gitversion.outputs.MajorMinorPatch }} \ + --title "Release ${{ steps.gitversion.outputs.MajorMinorPatch }}" \ + --notes "Release notes for version ${{ steps.gitversion.outputs.MajorMinorPatch }}" \ + quantower/Averages/bin/Release/Averages.dll \ + quantower/Statistics/bin/Release/Statistics.dll \ + quantower/Volatility/bin/Release/Volatility.dll \ + SyntheticVendor/bin/Release/SyntheticVendor.dll + + - name: Push release package to nuget.org + if: ${{ github.ref == 'refs/heads/main' }} + run: | + dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \ + --source https://api.nuget.org/v3/index.json \ + --skip-duplicate \ + --api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }} diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml deleted file mode 100644 index 513090d9..00000000 --- a/.github/workflows/codacy.yml +++ /dev/null @@ -1,61 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow checks out code, performs a Codacy security scan -# and integrates the results with the -# GitHub Advanced Security code scanning feature. For more information on -# the Codacy security scan action usage and parameters, see -# https://github.com/codacy/codacy-analysis-cli-action. -# For more information on Codacy Analysis CLI in general, see -# https://github.com/codacy/codacy-analysis-cli. - -name: Codacy Security Scan - -on: - push: - branches: [ "main" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "main" ] - schedule: - - cron: '17 22 * * 0' - -permissions: - contents: read - -jobs: - codacy-security-scan: - permissions: - contents: read # for actions/checkout to fetch code - security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - name: Codacy Security Scan - runs-on: ubuntu-latest - steps: - # Checkout the repository to the GitHub Actions runner - - name: Checkout code - uses: actions/checkout@v4 - - # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - - name: Run Codacy Analysis CLI - uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b - with: - # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository - # You can also omit the token and run the tools that support default configurations - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - verbose: true - output: results.sarif - format: sarif - # Adjust severity of non-security issues - gh-code-scanning-compat: true - # Force 0 exit code to allow SARIF file generation - # This will handover control about PR rejection to the GitHub side - max-allowed-issues: 2147483647 - - # Upload the SARIF file generated in the previous step - - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: results.sarif diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index ddf8b5b8..00000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,92 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL Advanced" - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - schedule: - - cron: '40 12 * * 1' - -jobs: - analyze: - name: Analyze (${{ matrix.language }}) - # Runner size impacts CodeQL analysis time. To learn more, please see: - # - https://gh.io/recommended-hardware-resources-for-running-codeql - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners (GitHub.com only) - # Consider using larger runners or machines with greater resources for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - permissions: - # required for all workflows - security-events: write - - # required to fetch internal or private CodeQL packs - packages: read - - # only required for workflows in private repositories - actions: read - contents: read - - strategy: - fail-fast: false - matrix: - include: - - language: csharp - build-mode: autobuild - # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' - # Use `c-cpp` to analyze code written in C, C++ or both - # Use 'java-kotlin' to analyze code written in Java, Kotlin or both - # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both - # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, - # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. - # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how - # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - build-mode: ${{ matrix.build-mode }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - if: matrix.build-mode == 'manual' - shell: bash - run: | - echo 'If you are using a "manual" build mode for one or more of the' \ - 'languages you are analyzing, replace this with the commands to build' \ - 'your code, for example:' - echo ' make bootstrap' - echo ' make release' - exit 1 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/workflows/main_automation.yml b/.github/workflows/main_automation.yml deleted file mode 100644 index e45f7eb5..00000000 --- a/.github/workflows/main_automation.yml +++ /dev/null @@ -1,174 +0,0 @@ -name: Stage/build/test/release/publish -on: - workflow_dispatch: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - build_test: - #runs-on: windows-latest - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - -############## Install tools - - - name: Create Quantower folder at root - run: | - sudo mkdir -p /Quantower/ - sudo chmod -R 777 /Quantower - - - name: Install .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: '8.x' - dotnet-quality: 'preview' - - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0 - with: - versionSpec: '6.x' - includePrerelease: true - - - name: Determine Version - id: gitversion - uses: gittools/actions/gitversion/execute@v0 - with: - useConfigFile: true - #configFilePath: GitVersion.yml - updateAssemblyInfo: true - -############## Install more tools - - - name: Install JDK11 for Sonar Scanner - uses: actions/setup-java@v3 - with: - java-version: 11 - distribution: 'zulu' - - - name: Install JetBrains - run: dotnet tool install JetBrains.dotCover.GlobalTool --global - - name: Install Sonar Scanner - run: dotnet tool install dotnet-sonarscanner --global - - name: Install dotnet-coverage - run: dotnet tool install dotnet-coverage --global - - - name: Sonar start - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: dotnet sonarscanner begin /o:"mihakralj" /k:"mihakralj_QuanTAlib" - /d:sonar.login="${{ secrets.SONAR_TOKEN }}" - /d:sonar.host.url="https://sonarcloud.io" - /d:sonar.cs.dotcover.reportsPaths=dotcover* - -############# Build and test - - - name: Build Main branch of QuanTAlib DLL - if: ${{ github.ref != 'refs/heads/dev' }} - run: dotnet build ./lib/quantalib.csproj --configuration Release --nologo -p:PackageVersion=${{ steps.gitversion.outputs.MajorMinorPatch }} - - name: Build dev branch of QuanTAlib DLL - if: ${{ github.ref == 'refs/heads/dev' }} - run: dotnet build ./lib/quantalib.csproj --configuration Release --nologo -p:PackageVersion=${{ steps.gitversion.outputs.FullSemVer }} - - name: Build Averages DLL - run: dotnet build ./quantower/Averages/Averages.csproj --configuration Release --nologo - - name: Build Statistics DLL - run: dotnet build ./quantower/Statistics/Statistics.csproj --configuration Release --nologo - - name: Build SyntheticVendor DLL - run: dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo - - - name: DotCover Test HTML - if: ${{ github.ref == 'refs/heads/dev' }} - run: dotnet dotcover test tests/tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html - - name: DotCover Test XML - if: ${{ github.ref == 'refs/heads/dev' }} - run: dotnet dotcover test tests/tests.csproj --dcReportType=DetailedXML --dcoutput=./dotcover.xml --verbosity=Detailed - - name: Coverlet Test - if: ${{ github.ref == 'refs/heads/dev' }} - run: dotnet test -p:CollectCoverage=true --collect:"XPlat Code Coverage" --results-directory "./" - -############## Report to Sonar/CodeCov/Codacy - - - name: Move coverage report to project root - if: ${{ github.ref == 'refs/heads/dev' }} - run: | - report=$(find . -name '*coverage.cobertura.xml' | head -1) - mv "$report" ./coverage.cobertura.xml - - - name: Upload to Codacy - if: ${{ github.ref == 'refs/heads/dev' }} - uses: codacy/codacy-coverage-reporter-action@v1 - with: - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: "*cover*.xml" - - - name: Upload to Codecov - if: ${{ github.ref == 'refs/heads/dev' }} - uses: codecov/codecov-action@v3 - with: - files: cover* - verbose: true - - - name: Upload to Sonar - if: ${{ github.ref == 'refs/heads/dev' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" - -############## Publish dev release - - - name: Publish dev release assets - if: ${{ github.ref == 'refs/heads/dev' }} - uses: SourceSprint/upload-multiple-releases@1.0.7 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - prerelease: true - overwrite: true - release_name: ${{ steps.gitversion.outputs.SemVer }} - tag_name: prerelease - release_config: | - lib/bin/Release/QuanTAlib.dll - quantower/Averages/bin/Release/Averages.dll - quantower/Statistics/bin/Release/Statistics.dll - SyntheticVendor/bin/Release/SyntheticVendor.dll - - - name: Push package to myget.org - run: dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' - --api-key ${{ secrets.MYGET_DEPLOY_KEY_QUANTALIB }} - --source https://www.myget.org/F/quantalib/api/v2/package - --skip-duplicate - -############## Publish main release - - - name: Publish main release assets - if: ${{ github.ref == 'refs/heads/main' }} - uses: SourceSprint/upload-multiple-releases@1.0.7 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - prerelease: false - overwrite: true - release_name: ${{ steps.gitversion.outputs.MajorMinorPatch }} - tag_name: latest - release_config: | - lib/bin/Release/QuanTAlib.dll - quantower/Averages/bin/Release/Averages.dll - quantower/Statistics/bin/Release/Statistics.dll - SyntheticVendor/bin/Release/SyntheticVendor.dll - - - name: Push package to nuget.org - if: ${{ github.ref == 'refs/heads/main' }} - run: dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' - --api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }} - --source https://api.nuget.org/v3/index.json - --skip-duplicate - diff --git a/.github/workflows/securitycodescan.yml b/.github/workflows/securitycodescan.yml deleted file mode 100644 index 16cfdf30..00000000 --- a/.github/workflows/securitycodescan.yml +++ /dev/null @@ -1,41 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow integrates SecurityCodeScan with GitHub's Code Scanning feature -# SecurityCodeScan is a vulnerability patterns detector for C# and VB.NET - -name: SecurityCodeScan - -on: - push: - branches: [ "main" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "main" ] - schedule: - - cron: '23 14 * * 2' - -jobs: - SCS: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: nuget/setup-nuget@04b0c2b8d1b97922f67eca497d7cf0bf17b8ffe1 - - uses: microsoft/setup-msbuild@v1.0.2 - - - name: Set up projects for analysis - uses: security-code-scan/security-code-scan-add-action@f8ff4f2763ed6f229eded80b1f9af82ae7f32a0d - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --no-restore - - - name: Convert sarif for uploading to GitHub - uses: security-code-scan/security-code-scan-results-action@cdb3d5e639054395e45bf401cba8688fcaf7a687 - - - name: Upload sarif - uses: github/codeql-action/upload-sarif@v3 diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml deleted file mode 100644 index 27cf35d9..00000000 --- a/.github/workflows/sonarcloud.yml +++ /dev/null @@ -1,67 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow helps you trigger a SonarCloud analysis of your code and populates -# GitHub Code Scanning alerts with the vulnerabilities found. -# Free for open source project. - -# 1. Login to SonarCloud.io using your GitHub account - -# 2. Import your project on SonarCloud -# * Add your GitHub organization first, then add your repository as a new project. -# * Please note that many languages are eligible for automatic analysis, -# which means that the analysis will start automatically without the need to set up GitHub Actions. -# * This behavior can be changed in Administration > Analysis Method. -# -# 3. Follow the SonarCloud in-product tutorial -# * a. Copy/paste the Project Key and the Organization Key into the args parameter below -# (You'll find this information in SonarCloud. Click on "Information" at the bottom left) -# -# * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN -# (On SonarCloud, click on your avatar on top-right > My account > Security -# or go directly to https://sonarcloud.io/account/security/) - -# Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/) -# or reach out to our community forum if you need some help (https://community.sonarsource.com/c/help/sc/9) - -name: SonarCloud analysis - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - workflow_dispatch: - -permissions: - pull-requests: read # allows SonarCloud to decorate PRs with analysis results - -jobs: - Analysis: - runs-on: ubuntu-latest - - steps: - - name: Analyze with SonarCloud - - # You can pin the exact commit or the version. - # uses: SonarSource/sonarcloud-github-action@v2.2.0 - uses: SonarSource/sonarcloud-github-action@4006f663ecaf1f8093e8e4abb9227f6041f52216 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) - with: - # Additional arguments for the SonarScanner CLI - args: - # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu) - # mandatory - -Dsonar.projectKey= mihakralj_QuanTAlib - -Dsonar.organization= mihakralj - # Comma-separated paths to directories containing main source files. - #-Dsonar.sources= # optional, default is project base directory - # Comma-separated paths to directories containing test source files. - #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/ - # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing. - #-Dsonar.verbose= # optional, default is false - # When you need the analysis to take place in a directory other than the one from which it was launched, default is . - projectBaseDir: . diff --git a/.sonarlint/QuanTAlib.json b/.sonarlint/QuanTAlib.json new file mode 100644 index 00000000..f75a1bc1 --- /dev/null +++ b/.sonarlint/QuanTAlib.json @@ -0,0 +1,4 @@ +{ + "sonarCloudOrganization": "mihakralj-quantalib", + "projectKey": "mihakralj_QuanTAlib" +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..dbbc4e17 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "sonarlint.connectedMode.connections.sonarcloud": [ + { + "organizationKey": "mihakralj", + "token": "6df7cd62a17dc4e1c5532df1da2f49d5a977dd50", + "connectionId": "mihakralj" + } + ], + "sarif-viewer.connectToGithubCodeScanning": "on", + "dotnet.dotnetPath": "C:/Program Files/dotnet", + "omnisharp.useModernNet": true, + "omnisharp.sdkPath": "C:/Program Files/dotnet/sdk", + "sonarlint.connectedMode.project": { + "connectionId": "mihakralj", + "projectKey": "mihakralj_QuanTAlib" + } +} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 5d40b927..df85d4db 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,11 +1,12 @@ - en-US net8.0 + preview + $(NoWarn);NU1903;NU5104 enable enable true - preview + en-US false false true @@ -21,17 +22,40 @@ AnyCPU true + + + true + link + true + true + true + none + true + true + true + false + true + false + false + false + false + true + false + true + + + S1944,S2053,S2222,S2259,S2583,S2589,S3329,S3655,S3900,S3949,S3966,S4158,S4347,S5773,S6781 + - - - all - runtime; build; native; contentfiles; analyzers - + + + + D:\Quantower $([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0]) - \ No newline at end of file + diff --git a/GitVersion.yml b/GitVersion.yml index 1eba6ce9..1fa72ce7 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,37 +1,18 @@ -assembly-versioning-scheme: MajorMinorPatch -assembly-file-versioning-scheme: MajorMinorPatch -mode: ContinuousDeployment -tag-prefix: '[vV]?' +next-version: 0.6.1 +mode: ContinuousDelivery major-version-bump-message: '\+semver:\s?(breaking|major)' minor-version-bump-message: '\+semver:\s?(feature|minor)' patch-version-bump-message: '\+semver:\s?(fix|patch)' no-bump-message: '\+semver:\s?(none|skip)' -commit-message-incrementing: Enabled - branches: main: regex: ^main$ - mode: ContinuousDeployment - label: '' + mode: ContinuousDelivery increment: Patch - track-merge-target: false - source-branches: [] - tracks-release-branches: false - is-release-branch: true - is-main-branch: true - pre-release-weight: 55000 - dev: regex: ^dev(elop)?(ment)?$ - mode: ContinuousDelivery - label: dev - increment: Patch - track-merge-target: true - source-branches: ['main'] - tracks-release-branches: true - is-release-branch: false - is-main-branch: false - pre-release-weight: 0 - + mode: ContinuousDeployment + increment: Inherit ignore: - sha: [] \ No newline at end of file + sha: [] +merge-message-formats: {} diff --git a/QuanTAlib.sln b/QuanTAlib.sln index 44dd6e71..068fcd79 100644 --- a/QuanTAlib.sln +++ b/QuanTAlib.sln @@ -1,19 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +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", "{584E06A9-CEB4-476A-85CC-6A8FF3974AE2}" +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", "{D85FEBB4-B651-466F-85CC-FD902378D4D2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{1B9AC248-76F8-44DD-958D-F1DC08EE1E87}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MovingAverages", "quantower\averages\Averages.csproj", "{32CC09CC-26E3-4FCE-8932-C0513C4AD766}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\_Statistics.csproj", "{2E9427C7-144F-488E-A29D-789ACC1C32AE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{20B1B5F1-8C36-4668-B0AE-951C13AE197B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages\_Averages.csproj", "{6BE10C39-4127-446C-818B-7976FCDD51D5}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{A8D9AE68-24E3-476C-BB98-244541BB4B43}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\_Volatility.csproj", "{B7DC44F7-D3A3-4C70-9025-513E0182B646}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{B6D3EB11-63B6-430F-B526-E1981B3D8214}" +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", "{2D97C971-20BF-40DB-94AA-3279F787D3CB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,28 +25,35 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Release|Any CPU.Build.0 = Release|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Release|Any CPU.Build.0 = Release|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Release|Any CPU.Build.0 = Release|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Release|Any CPU.Build.0 = Release|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.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.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 + {2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D97C971-20BF-40DB-94AA-3279F787D3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D97C971-20BF-40DB-94AA-3279F787D3CB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {B6D3EB11-63B6-430F-B526-E1981B3D8214} = {A8D9AE68-24E3-476C-BB98-244541BB4B43} + {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 diff --git a/SyntheticVendor/SyntheticVendor.cs b/SyntheticVendor/SyntheticVendor.cs index 58ad336b..913041a4 100644 --- a/SyntheticVendor/SyntheticVendor.cs +++ b/SyntheticVendor/SyntheticVendor.cs @@ -3,18 +3,19 @@ using System.Collections.Generic; using System.Threading; using TradingPlatform.BusinessLayer; using TradingPlatform.BusinessLayer.Integration; +using System.Diagnostics.CodeAnalysis; -namespace SyntheticVendorNamespace +namespace SyntheticVendorNamespace; + +public class SyntheticVendor : Vendor { - public class SyntheticVendor : Vendor - { - private readonly List exchanges; - private readonly List assets; - private readonly List symbols; + private readonly List exchanges; + private readonly List assets; + private readonly List symbols; - public SyntheticVendor() - { - exchanges = new List + public SyntheticVendor() + { + exchanges = new List { //Spike, @@ -42,13 +43,13 @@ namespace SyntheticVendorNamespace new MessageExchange { Id = "QT", ExchangeName = "6 QuanTAlib" } }; - assets = new List + assets = new List { new MessageAsset { Id = "USD", Name = "USD" }, }; - symbols = new List + symbols = new List { CreateMessageSymbol(id: "W1", name: "1 Digital spike", exchangeId: "QT", assetId: "USD", type: SymbolType.Crypto, description: "Sudden sharp spike in the signal"), @@ -73,300 +74,303 @@ namespace SyntheticVendorNamespace CreateMessageSymbol("W17", "2 Geometric Brownian motion", "QT", "USD", SymbolType.Synthetic) }; -/* - Bond, - CFD, - Crypto, - Debentures, - Equities, - ETF, - FixedIncome, - Forex, - Forward, - Futures, - Indexes, - Options, - Spot, - Synthetic, - Swap, - Warrants, + /* + Bond, + CFD, + Crypto, + Debentures, + Equities, + ETF, + FixedIncome, + Forex, + Forward, + Futures, + Indexes, + Options, + Spot, + Synthetic, + Swap, + Warrants, -*/ + */ - } + } - private MessageSymbol CreateMessageSymbol( - string id, - string name, - string exchangeId, - string assetId, - SymbolType type, - string description) + public static VendorMetaData GetVendorMetaData() + { + return new VendorMetaData() { - var messageSymbol = new MessageSymbol(id) + VendorName = "Synthetic Vendor", + VendorDescription = "A synthetic vendor for testing and demonstration purposes", + GetDefaultConnections = () => { - Name = name, - Description = description, - SymbolType = type, - ExchangeId = exchangeId, - ProductAssetId = assetId, + var defaultConnection = Vendor.CreateDefaultConnectionInfo( + "Synthetic Connection", + "Synthetic Vendor", + "", // Replace with actual path if you have a logo + allowCreateCustomConnections: true + ); + return new List { defaultConnection }; + } + }; + } - // Setting some default values - QuotingCurrencyAssetID = "USD", - HistoryType = HistoryType.Last, - DeltaCalculationType = DeltaCalculationType.TickDirection, - LotSize = 1, - VariableTickList = new List + private MessageSymbol CreateMessageSymbol( + string id, + string name, + string exchangeId, + string assetId, + SymbolType type, + string description) + { + var messageSymbol = new MessageSymbol(id) + { + Name = name, + Description = description, + SymbolType = type, + ExchangeId = exchangeId, + ProductAssetId = assetId, + + // Setting some default values + QuotingCurrencyAssetID = "USD", + HistoryType = HistoryType.Last, + DeltaCalculationType = DeltaCalculationType.TickDirection, + LotSize = 1, + VariableTickList = new List { new VariableTick(0.01) // Default tick size } - }; + }; - return messageSymbol; - } + return messageSymbol; + } - public static VendorMetaData GetVendorMetaData() + + + + private MessageSymbol CreateMessageSymbol(string id, string name, string exchangeId, string assetId, SymbolType type) + { + return new MessageSymbol(id) { - return new VendorMetaData() - { - VendorName = "Synthetic Vendor", - VendorDescription = "A synthetic vendor for testing and demonstration purposes", - GetDefaultConnections = () => - { - var defaultConnection = Vendor.CreateDefaultConnectionInfo( - "Synthetic Connection", - "Synthetic Vendor", - "", // Replace with actual path if you have a logo - allowCreateCustomConnections: true - ); - return new List { defaultConnection }; - } - }; - } + Name = name, + ExchangeId = exchangeId, + ProductAssetId = assetId, + QuotingCurrencyAssetID = "USD", + QuotingType = SymbolQuotingType.LotSize, + LotSize = 1, + NettingType = NettingType.OnePosition, + VolumeType = SymbolVolumeType.Volume, + AllowCalculateRealtimeTicks = true, + AllowCalculateRealtimeTrades = false, + AllowCalculateRealtimeVolume = true, + AllowCalculateRealtimeChange = true, + AllowAbbreviatePriceByTickSize = false, + NotionalValueStep = 0.01, + DeltaCalculationType = DeltaCalculationType.AggressorFlag, // Changed from None to AggressorFlag + MinVolumeAnalysisTickSize = 0.01, + MaturityDate = DateTime.MaxValue, // Set to max value for non-expiring symbols + HistoryType = HistoryType.Last, + MinLot = 0.01, + LotStep = 0.01, + MaxLot = 1000000, + SymbolType = type + /* + SymbolType.Unknown, + [EnumMember] Forex, + [EnumMember] Equities, + [EnumMember] CFD, + [EnumMember] Indexes, + [EnumMember] Futures, + [EnumMember] Options, + [EnumMember] ETF, + [EnumMember] Crypto, + [EnumMember] Synthetic, + [EnumMember] Spot, + [EnumMember] Forward, + [EnumMember] FixedIncome, + [EnumMember] Warrants, + [EnumMember] Debentures, + [EnumMember] Bond, + [EnumMember] Swap, + */ + }; + } - private MessageSymbol CreateMessageSymbol(string id, string name, string exchangeId, string assetId, SymbolType type) - { - return new MessageSymbol(id) - { - Name = name, - ExchangeId = exchangeId, - ProductAssetId = assetId, - QuotingCurrencyAssetID = "USD", - QuotingType = SymbolQuotingType.LotSize, - LotSize = 1, - NettingType = NettingType.OnePosition, - VolumeType = SymbolVolumeType.Volume, - AllowCalculateRealtimeTicks = true, - AllowCalculateRealtimeTrades = false, - AllowCalculateRealtimeVolume = true, - AllowCalculateRealtimeChange = true, - AllowAbbreviatePriceByTickSize = false, - NotionalValueStep = 0.01, - DeltaCalculationType = DeltaCalculationType.AggressorFlag, // Changed from None to AggressorFlag - MinVolumeAnalysisTickSize = 0.01, - MaturityDate = DateTime.MaxValue, // Set to max value for non-expiring symbols - HistoryType = HistoryType.Last, - MinLot = 0.01, - LotStep = 0.01, - MaxLot = 1000000, - SymbolType = type -/* - SymbolType.Unknown, - [EnumMember] Forex, - [EnumMember] Equities, - [EnumMember] CFD, - [EnumMember] Indexes, - [EnumMember] Futures, - [EnumMember] Options, - [EnumMember] ETF, - [EnumMember] Crypto, - [EnumMember] Synthetic, - [EnumMember] Spot, - [EnumMember] Forward, - [EnumMember] FixedIncome, - [EnumMember] Warrants, + public override ConnectionResult Connect(ConnectRequestParameters connectRequestParameters) + { + // Simulating connection process + Thread.Sleep(100); // Simulate some connection delay - [EnumMember] Debentures, - [EnumMember] Bond, - [EnumMember] Swap, -*/ - }; - } + return ConnectionResult.CreateSuccess("Successfully connected to Synthetic Vendor"); + } - public override ConnectionResult Connect(ConnectRequestParameters connectRequestParameters) - { - // Simulating connection process - Thread.Sleep(100); // Simulate some connection delay + public override void Disconnect() + { + // Simulating disconnection process + Thread.Sleep(500); // Simulate some disconnection delay - return ConnectionResult.CreateSuccess("Successfully connected to Synthetic Vendor"); - } + } - public override void Disconnect() - { - // Simulating disconnection process - Thread.Sleep(500); // Simulate some disconnection delay - - } - - public override PingResult Ping() - { + public override PingResult Ping() + { return new PingResult() + { + State = PingEnum.Connected, + PingTime = TimeSpan.FromMilliseconds(2), + RoundTripTime = TimeSpan.FromMilliseconds(2) + }; + } + + + public override IList GetExchanges(CancellationToken token) + { + return exchanges; + } + + public override IList GetAssets(CancellationToken token) + { + return assets; + } + + public override IList GetSymbols(CancellationToken token) + { + return symbols; + } + + public override void SubscribeSymbol(SubscribeQuotesParameters parameters) + { + // Empty method for data subscription to be filled later + } + + public override void UnSubscribeSymbol(SubscribeQuotesParameters parameters) + { + // Empty method for data unsubscription to be filled later + } + + + public override IList LoadHistory(HistoryRequestParameters requestParameters) + { + var historyItems = new List(); + var symbolId = requestParameters.SymbolId; + + if (string.IsNullOrEmpty(symbolId)) return historyItems; + + DateTime from = requestParameters.FromTime; + DateTime to = requestParameters.ToTime; + + TimeSpan periodTimeSpan = requestParameters.Aggregation.GetPeriod.Duration; + + // Define the maximum number of items to generate per request + const int MAX_ITEMS_PER_REQUEST = 10000; + + Func waveGenerator = GetWaveGenerator(symbolId); + + DateTime currentTime = from; + while (currentTime < to) + { + DateTime intervalEnd = currentTime.AddTicks(periodTimeSpan.Ticks * MAX_ITEMS_PER_REQUEST); + if (intervalEnd > to) + intervalEnd = to; + + while (currentTime <= intervalEnd) { - State = PingEnum.Connected, - PingTime = TimeSpan.FromMilliseconds(2), - RoundTripTime = TimeSpan.FromMilliseconds(2) - }; - } + var historyItem = waveGenerator(currentTime, periodTimeSpan); //calling generator fuction + historyItems.Add(historyItem); + currentTime = currentTime.Add(periodTimeSpan); - - - public override void OnConnected(CancellationToken token) - { - // This method is called after a successful connection - // You can initialize resources or start any necessary processes here - base.OnConnected(token); - - // For example, you might want to push some initial messages or data - // PushMessage(new MessageVendorEvent("SyntheticVendor connected successfully")); - } - - - - public override IList GetExchanges(CancellationToken token) - { - return exchanges; - } - - public override IList GetAssets(CancellationToken token) - { - return assets; - } - - public override IList GetSymbols(CancellationToken token) - { - return symbols; - } - - public override void SubscribeSymbol(SubscribeQuotesParameters parameters) - { - // Empty method for data subscription to be filled later - } - - public override void UnSubscribeSymbol(SubscribeQuotesParameters parameters) - { - // Empty method for data unsubscription to be filled later - } - - - public override IList LoadHistory(HistoryRequestParameters requestParameters) - { - var historyItems = new List(); - var symbolId = requestParameters.SymbolId; - - if (string.IsNullOrEmpty(symbolId)) return historyItems; - - DateTime from = requestParameters.FromTime; - DateTime to = requestParameters.ToTime; - - TimeSpan periodTimeSpan = requestParameters.Aggregation.GetPeriod.Duration; - - // Define the maximum number of items to generate per request - const int MAX_ITEMS_PER_REQUEST = 10000; - - Func waveGenerator = GetWaveGenerator(symbolId); - - DateTime currentTime = from; - while (currentTime < to) - { - DateTime intervalEnd = currentTime.AddTicks(periodTimeSpan.Ticks * MAX_ITEMS_PER_REQUEST); - if (intervalEnd > to) - intervalEnd = to; - - while (currentTime <= intervalEnd) - { - var historyItem = waveGenerator(currentTime, periodTimeSpan); //calling generator fuction - historyItems.Add(historyItem); - - currentTime = currentTime.Add(periodTimeSpan); - - if (requestParameters.CancellationToken.IsCancellationRequested) return historyItems; - } - - currentTime = intervalEnd; + if (requestParameters.CancellationToken.IsCancellationRequested) return historyItems; } - return historyItems; + currentTime = intervalEnd; } - private Func GetWaveGenerator(string symbolId) + return historyItems; + } + + private Func GetWaveGenerator(string symbolId) + { + switch (symbolId) { - switch (symbolId) - { - //case "W0": return GenerateConstant; - case "W1": return GenerateSpike; - case "W2": return GenerateDiracDelta; - case "W3": return GenerateSquareWave; - case "W4": return GenerateSawtoothWave; - case "W5": return GenerateInverseSawtoothWave; - case "W6": return GenerateTriangleWave; - case "W7": return GenerateSineWave; - case "W8": return GenerateSincWave; - case "W9": return GenerateGaussianPulse; - case "W10": return GenerateFrequencySweep; - case "W11": return GenerateAMSignal; - case "W12": return GenerateFMSignal; - case "W13": return GenerateWhiteNoise; - case "W14": return GeneratePinkNoise; - case "W15": return GenerateBrownNoise; - case "W16": return GenerateFBM; - case "W17": return GenerateGBM; + case "W1": return GenerateSpike; + case "W2": return GenerateDiracDelta; + case "W3": return GenerateSquareWave; + case "W4": return GenerateSawtoothWave; + case "W5": return GenerateInverseSawtoothWave; + case "W6": return GenerateTriangleWave; + case "W7": return GenerateSineWave; + case "W8": return GenerateSincWave; + case "W9": return GenerateGaussianPulse; + case "W10": return GenerateFrequencySweep; + case "W11": return GenerateAMSignal; + case "W12": return GenerateFMSignal; + case "W13": return GenerateWhiteNoise; + case "W14": return GeneratePinkNoise; + case "W15": return GenerateBrownNoise; + case "W16": return GenerateFBM; + case "W17": return GenerateGBM; - default: return GenerateSineWave; - } + default: return GenerateSineWave; } + } - public override HistoryMetadata GetHistoryMetadata(CancellationToken cancellationToken) +/* + public override HistoryMetadata GetHistoryMetadata(CancellationToken cancellationToken) + { + return new HistoryMetadata { - return new HistoryMetadata() + AllowedAggregations = new string[] { "Time", "Tick" }, + AllowedPeriodsHistoryAggregationTime = new Period[] { - AllowedHistoryTypes = new HistoryType[] - { - HistoryType.Bid, - HistoryType.Ask, - HistoryType.Midpoint, - HistoryType.Last, - HistoryType.BidAsk, - HistoryType.Mark, - }, - AllowedPeriods = new Period[] - { - Period.TICK1, - Period.SECOND1, Period.SECOND5, Period.SECOND10, Period.SECOND15, Period.SECOND30, - Period.MIN1, Period.MIN2, Period.MIN3, Period.MIN4, Period.MIN5, - Period.MIN10, Period.MIN15, Period.MIN30, - Period.HOUR1, Period.HOUR2, Period.HOUR3, Period.HOUR4, - Period.HOUR6, Period.HOUR8, Period.HOUR12, - Period.DAY1, - Period.WEEK1, - Period.MONTH1, - Period.YEAR1 - }, - UseHistoryLocalCache = false - }; - } + Period.SECOND1, Period.SECOND5, Period.SECOND10, Period.SECOND15, Period.SECOND30, + Period.MIN1, Period.MIN2, Period.MIN3, Period.MIN4, Period.MIN5, + Period.MIN10, Period.MIN15, Period.MIN30, + Period.HOUR1, Period.HOUR2, Period.HOUR3, Period.HOUR4, + Period.HOUR6, Period.HOUR8, Period.HOUR12, + Period.DAY1, + Period.WEEK1, + Period.MONTH1, + Period.YEAR1 + }, + AllowedBasePeriodsHistoryAggregationTime = new BasePeriod[] + { + BasePeriod.Second, BasePeriod.Minute, BasePeriod.Hour, BasePeriod.Day, BasePeriod.Week, BasePeriod.Month, BasePeriod.Year + }, + AllowedHistoryTypesHistoryAggregationTime = new HistoryType[] + { + HistoryType.Bid, + HistoryType.Ask, + HistoryType.Midpoint, + HistoryType.Last, + HistoryType.BidAsk, + HistoryType.Mark + }, + AllowedHistoryTypesHistoryAggregationTick = new HistoryType[] + { + HistoryType.Bid, + HistoryType.Ask, + HistoryType.Midpoint, + HistoryType.Last, + HistoryType.BidAsk, + HistoryType.Mark + }, + DegreeOfParallelism = 1, + UseHistoryLocalCache = false, + BuildUncompletedBars = true + }; + } +*/ - -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ private HistoryItemBar GenerateSpike(DateTime time, TimeSpan slice) { @@ -414,555 +418,549 @@ namespace SyntheticVendorNamespace + private HistoryItemBar GenerateDiracDelta(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); - private static readonly double[] distributionValues = new double[] + // Calculate the start of the current day + DateTime dayStart = utcTime.Date; + + // Determine which bar of the day we're on + int barOfDay = (int)((utcTime - dayStart).Ticks / slice.Ticks); + + double openValue, closeValue; + double scaleFactor = 100; // Scale factor to convert to percentage + + // Generate the spike pattern for the first 4 bars of each day + switch (barOfDay) { - 0.010, // Extreme left tail - 0.050, // Left tail - 0.200, // Left of center - 0.480, // Center (peak) - 0.200, // Right of center - 0.050, // Right tail - 0.010 // Extreme right tail + case 0: + openValue = 0.000001 * scaleFactor; + closeValue = 0.05 * scaleFactor; + break; + case 1: + openValue = 0.05 * scaleFactor; + closeValue = 0.50 * scaleFactor; + break; + case 2: + openValue = 0.50 * scaleFactor; + closeValue = 0.05 * scaleFactor; + break; + case 3: + openValue = 0.05 * scaleFactor; + closeValue = 0.0000001 * scaleFactor; + break; + default: + // Outside of the spike period, use baseline value + openValue = closeValue = 0.000001; + break; + } + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = openValue, + High = Math.Max(openValue, closeValue), + Low = Math.Min(openValue, closeValue), + Close = closeValue, + Volume = Math.Abs(closeValue - openValue), + Ticks = time.Add(slice).Ticks - time.Ticks + }; + } + + + + private HistoryItemBar GenerateSineWave(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); + + // Calculate the number of hours since the epoch + double minutesSinceEpoch = (utcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMinutes; + + // Calculate the position within the 25-hour cycle + double cyclePosition = minutesSinceEpoch % 1500; + + + // Calculate the sine wave values + double frequency = 2 * Math.PI / 1500; // Complete cycle over 25 hours + double value = 50 + 50 * Math.Sin(cyclePosition * frequency); // Oscillate between 0 and 100 + double nextValue = 50 + 50 * Math.Sin((cyclePosition + slice.TotalMinutes) * frequency); + + double factor = 0.6 * Math.Abs(nextValue - value); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + + High = Math.Max(value, nextValue) + factor, + Low = Math.Min(value, nextValue) - factor, + + Close = nextValue, + Volume = Math.Abs(nextValue - value) * 100, // Volume proportional to price change + Ticks = time.Add(slice).Ticks - time.Ticks + }; + } + + + private HistoryItemBar GenerateSquareWave(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); + + // Calculate the time within the day (in hours) + double hoursInDay = utcTime.TimeOfDay.TotalHours; + + double openValue, closeValue; + + if (hoursInDay < 12) + { + // First half of the day + openValue = 99; + closeValue = 100; + } + else + { + // Second half of the day + openValue = 1; + closeValue = 0.0001; + } + + // Handle transition bars + if (Math.Abs(hoursInDay - 12) < slice.TotalHours / 2) + { + // Transition from 100 to 0 at noon + openValue = 100; + closeValue = 0.0001; + } + else if (hoursInDay < slice.TotalHours / 2 || hoursInDay > 24 - slice.TotalHours / 2) + { + // Transition from 0 to 100 at midnight + openValue = 0.0001; + closeValue = 100; + } + else + { + // No action + } + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = openValue, + High = Math.Max(openValue, closeValue), + Low = Math.Min(openValue, closeValue), + Close = closeValue, + Volume = Math.Abs(closeValue - openValue), + Ticks = time.Add(slice).Ticks - time.Ticks + }; + } + + private HistoryItemBar GenerateSawtoothWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; // 24-hour period + double position = hours % period; + double value = 200 * (position / period) - 100; + double nextValue = 200 * ((position + slice.TotalHours) % period / period) - 100; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GenerateInverseSawtoothWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; // 24-hour period + double position = hours % period; + double value = 100 - (200 * (position / period)); + double nextValue = 100 - (200 * ((position + slice.TotalHours) % period / period)); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GeneratePulseWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; // 24-hour period + double position = hours % period; + double value = position < period / 5 ? 100 : -100; // 20% duty cycle + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = 100, + Low = -100, + Close = value, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GenerateTriangleWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; + double position = hours % period; + double value = 200 * (Math.Abs(position / period - 0.5) - 0.25) * 100; + double nextValue = 200 * (Math.Abs(((position + slice.TotalHours) % period) / period - 0.5) - 0.25) * 100; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GenerateSincWave(DateTime time, TimeSpan slice) + { + double minutes = (time - DateTime.UnixEpoch).TotalMinutes; + double period = 1500.0; // 24-hour period + double frequency = 2 * Math.PI / period; // Full cycle over 24 hours + + // Adjust time to center the main peak at 12 hours + double t = minutes % period - period / 2; + + // Scale factor + double scaleFactor = 7.0; + + // Calculate Sinc value + double x = scaleFactor * frequency * t; + double sincValue = x != 0 ? 100 * Math.Sin(x) / x : 100; + + // Calculate next value + double nextT = ((minutes + slice.TotalMinutes) % period) - period / 2; + double nextX = scaleFactor * frequency * nextT; + double nextSincValue = nextX != 0 ? 100 * Math.Sin(nextX) / nextX : 100; + + // Ensure minimum value + double minValue = 0.00001; + sincValue = Math.Sign(sincValue) * Math.Max(Math.Abs(sincValue), minValue); + nextSincValue = Math.Sign(nextSincValue) * Math.Max(Math.Abs(nextSincValue), minValue); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = sincValue, + High = Math.Max(sincValue, nextSincValue), + Low = Math.Min(sincValue, nextSincValue), + Close = nextSincValue, + Volume = Math.Abs(nextSincValue - sincValue), // Volume as the change in value + Ticks = slice.Ticks + }; + } + + private HistoryItemBar GenerateGaussianPulse(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double totalPeriod = 24.0; // 24-hour total cycle + double pulsePeriod = 12.0; // 12-hour pulse duration + double position = hours % totalPeriod; + + // Parameters for the Gaussian pulse + double amplitude = 100.0; // Maximum amplitude + double center = pulsePeriod / 2.0; // Center of the pulse (at 6 hours within the pulse period) + double width = pulsePeriod / 6.0; // Width of the pulse (adjusts the spread) + + double baselineValue = 0.00001; // Value outside the pulse period + + // Calculate the Gaussian pulse value + double value; + if (position < pulsePeriod) + { + value = amplitude * Math.Exp(-Math.Pow(position - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue; + } + else + { + value = baselineValue; + } + + // Calculate the next value for the slice + double nextPosition = (hours + slice.TotalHours) % totalPeriod; + double nextValue; + if (nextPosition < pulsePeriod) + { + nextValue = amplitude * Math.Exp(-Math.Pow(nextPosition - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue; + } + else + { + nextValue = baselineValue; + } + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = Math.Abs(nextValue - value), // Volume as the change in value + Ticks = slice.Ticks + }; + } + + private HistoryItemBar GenerateFrequencySweep(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double sweepPeriod = 48.0; // 48-hour period + + // Starting frequency (very low) + double minFreq = Math.PI / 48.0; + + // Calculate the ending frequency to ensure continuity + double maxFreq = Math.PI * 1.0 * Math.Exp(2 * Math.PI / sweepPeriod); + + // Calculate the exponential factor for frequency sweep + double expFactor = Math.Log(maxFreq / minFreq) / sweepPeriod; + + // Calculate the overall phase up to the current time + double totalPhase = (minFreq / expFactor) * (Math.Exp(expFactor * (hours % sweepPeriod)) - 1); + + // Shift the phase to start the cycle at 100 (cosine-like behavior) + totalPhase += Math.PI / 2; + + // Calculate the value of the signal at the current time + double value = 100.0 * Math.Sin(totalPhase); + + // Calculate the value of the signal at the end of the slice + double nextPhase = (minFreq / expFactor) * (Math.Exp(expFactor * ((hours + slice.TotalHours) % sweepPeriod)) - 1); + nextPhase += Math.PI / 2; // Apply the same phase shift + double nextValue = 100.0 * Math.Sin(nextPhase); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = Math.Abs(nextValue - value), // Volume as the change in value + Ticks = slice.Ticks + }; + } + +#pragma warning disable S2245 + // NOSONAR + readonly Random random = new Random(); +#pragma warning restore S2245 + private double currentAmplitude = 100; + private HistoryItemBar GenerateAMSignal(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 12.0; + double frequency = 2 * Math.PI / period; // Frequency for a 5-hour period + + // Determine the start of the current 5-hour cycle + double cycleStartTime = Math.Floor(hours / period) * period; + + // Calculate the phase of the signal within the current 5-hour cycle + double phase = frequency * (hours % period); + + // If we're at the start of a new 5-hour cycle, generate a new amplitude + if (hours % period == 0) + { + currentAmplitude = random.NextDouble() * 100; + } + + // Calculate the value of the signal at the current time + double value = currentAmplitude * Math.Sin(phase); + + // Calculate the value of the signal at the end of the slice + double nextPhase = frequency * ((hours + slice.TotalHours) % period); + double nextValue = currentAmplitude * Math.Sin(nextPhase); + + // Create the HistoryItemBar + var historyItem = new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, // Set Close to the newly calculated value + Volume = Math.Abs(nextValue), // Volume as the change in value + Ticks = slice.Ticks }; - private HistoryItemBar GenerateDiracDelta(DateTime time, TimeSpan slice) - { - // Ensure we're working with UTC time - DateTime utcTime = time.ToUniversalTime(); + return historyItem; + } - // Calculate the start of the current day - DateTime dayStart = utcTime.Date; - // Determine which bar of the day we're on - int barOfDay = (int)((utcTime - dayStart).Ticks / slice.Ticks); + private double currentFrequency = Math.PI / 220.0; // Initial frequency + private double accumulatedPhase = 0; + private double lastCloseValue = 0; // To store the last close value - double openValue, closeValue; - double scaleFactor = 100; // Scale factor to convert to percentage - - // Generate the spike pattern for the first 4 bars of each day - switch (barOfDay) - { - case 0: - openValue = 0.000001 * scaleFactor; - closeValue = 0.05 * scaleFactor; - break; - case 1: - openValue = 0.05 * scaleFactor; - closeValue = 0.50 * scaleFactor; - break; - case 2: - openValue = 0.50 * scaleFactor; - closeValue = 0.05 * scaleFactor; - break; - case 3: - openValue = 0.05 * scaleFactor; - closeValue = 0.0000001 * scaleFactor; - break; - default: - // Outside of the spike period, use baseline value - openValue = closeValue = 0.000001; - break; - } - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = openValue, - High = Math.Max(openValue, closeValue), - Low = Math.Min(openValue, closeValue), - Close = closeValue, - Volume = Math.Abs(closeValue - openValue), - Ticks = time.Add(slice).Ticks - time.Ticks - }; - } - - - - private HistoryItemBar GenerateSineWave(DateTime time, TimeSpan slice) - { - // Ensure we're working with UTC time - DateTime utcTime = time.ToUniversalTime(); - - // Calculate the number of hours since the epoch - double minutesSinceEpoch = (utcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMinutes; - - // Calculate the position within the 25-hour cycle - double cyclePosition = minutesSinceEpoch % 1500; - - - // Calculate the sine wave values - double frequency = 2 * Math.PI / 1500; // Complete cycle over 25 hours - double value = 50 + 50 * Math.Sin(cyclePosition * frequency); // Oscillate between 0 and 100 - double nextValue = 50 + 50 * Math.Sin((cyclePosition + slice.TotalMinutes) * frequency); - - double factor = 0.6 * Math.Abs (nextValue - value); - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - - High = Math.Max(value, nextValue)+factor, - Low = Math.Min(value, nextValue)-factor, - - Close = nextValue, - Volume = Math.Abs(nextValue - value) * 100, // Volume proportional to price change - Ticks = time.Add(slice).Ticks - time.Ticks - }; - } - - - private HistoryItemBar GenerateSquareWave(DateTime time, TimeSpan slice) - { - // Ensure we're working with UTC time - DateTime utcTime = time.ToUniversalTime(); - - // Calculate the time within the day (in hours) - double hoursInDay = utcTime.TimeOfDay.TotalHours; - - double openValue, closeValue; - - if (hoursInDay < 12) - { - // First half of the day - openValue = 99; - closeValue = 100; - } - else - { - // Second half of the day - openValue = 1; - closeValue = 0.0001; - } - - // Handle transition bars - if (Math.Abs(hoursInDay - 12) < slice.TotalHours / 2) - { - // Transition from 100 to 0 at noon - openValue = 100; - closeValue = 0.0001; - } - else if (hoursInDay < slice.TotalHours / 2 || hoursInDay > 24 - slice.TotalHours / 2) - { - // Transition from 0 to 100 at midnight - openValue = 0.0001; - closeValue = 100; - } - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = openValue, - High = Math.Max(openValue, closeValue), - Low = Math.Min(openValue, closeValue), - Close = closeValue, - Volume = Math.Abs(closeValue - openValue), - Ticks = time.Add(slice).Ticks - time.Ticks - }; - } - - private HistoryItemBar GenerateSawtoothWave(DateTime time, TimeSpan slice) - { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double period = 24; // 24-hour period - double position = hours % period; - double value = 200 * (position / period) - 100; - double nextValue = 200 * ((position + slice.TotalHours) % period / period) - 100; - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - High = Math.Max(value, nextValue), - Low = Math.Min(value, nextValue), - Close = nextValue, - Volume = 100, - Ticks = 100 - }; - } - - private HistoryItemBar GenerateInverseSawtoothWave(DateTime time, TimeSpan slice) - { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double period = 24; // 24-hour period - double position = hours % period; - double value = 100 - (200 * (position / period)); - double nextValue = 100 - (200 * ((position + slice.TotalHours) % period / period)); - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - High = Math.Max(value, nextValue), - Low = Math.Min(value, nextValue), - Close = nextValue, - Volume = 100, - Ticks = 100 - }; - } - - private HistoryItemBar GeneratePulseWave(DateTime time, TimeSpan slice) - { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double period = 24; // 24-hour period - double position = hours % period; - double value = position < period / 5 ? 100 : -100; // 20% duty cycle - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - High = 100, - Low = -100, - Close = value, - Volume = 100, - Ticks = 100 - }; - } - - private HistoryItemBar GenerateTriangleWave(DateTime time, TimeSpan slice) - { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double period = 24; - double position = hours % period; - double value = 200 * (Math.Abs(position / period - 0.5) - 0.25) * 100; - double nextValue = 200 * (Math.Abs(((position + slice.TotalHours) % period) / period - 0.5) - 0.25) * 100; - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - High = Math.Max(value, nextValue), - Low = Math.Min(value, nextValue), - Close = nextValue, - Volume = 100, - Ticks = 100 - }; - } - - private HistoryItemBar GenerateSincWave(DateTime time, TimeSpan slice) - { - double minutes = (time - DateTime.UnixEpoch).TotalMinutes; - double period = 1500.0; // 24-hour period - double frequency = 2 * Math.PI / period; // Full cycle over 24 hours - - // Adjust time to center the main peak at 12 hours - double t = minutes % period - period / 2; - - // Scale factor - double scaleFactor = 7.0; - - // Calculate Sinc value - double x = scaleFactor * frequency * t; - double sincValue = x != 0 ? 100 * Math.Sin(x) / x : 100; - - // Calculate next value - double nextT = ((minutes + slice.TotalMinutes) % period) - period / 2; - double nextX = scaleFactor * frequency * nextT; - double nextSincValue = nextX != 0 ? 100 * Math.Sin(nextX) / nextX : 100; - - // Ensure minimum value - double minValue = 0.00001; - sincValue = Math.Sign(sincValue) * Math.Max(Math.Abs(sincValue), minValue); - nextSincValue = Math.Sign(nextSincValue) * Math.Max(Math.Abs(nextSincValue), minValue); - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = sincValue, - High = Math.Max(sincValue, nextSincValue), - Low = Math.Min(sincValue, nextSincValue), - Close = nextSincValue, - Volume = Math.Abs(nextSincValue - sincValue), // Volume as the change in value - Ticks = slice.Ticks - }; - } - - private HistoryItemBar GenerateGaussianPulse(DateTime time, TimeSpan slice) - { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double totalPeriod = 24.0; // 24-hour total cycle - double pulsePeriod = 12.0; // 12-hour pulse duration - double position = hours % totalPeriod; - - // Parameters for the Gaussian pulse - double amplitude = 100.0; // Maximum amplitude - double center = pulsePeriod / 2.0; // Center of the pulse (at 6 hours within the pulse period) - double width = pulsePeriod / 6.0; // Width of the pulse (adjusts the spread) - - double baselineValue = 0.00001; // Value outside the pulse period - - // Calculate the Gaussian pulse value - double value; - if (position < pulsePeriod) - { - value = amplitude * Math.Exp(-Math.Pow(position - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue; - } - else - { - value = baselineValue; - } - - // Calculate the next value for the slice - double nextPosition = (hours + slice.TotalHours) % totalPeriod; - double nextValue; - if (nextPosition < pulsePeriod) - { - nextValue = amplitude * Math.Exp(-Math.Pow(nextPosition - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue; - } - else - { - nextValue = baselineValue; - } - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - High = Math.Max(value, nextValue), - Low = Math.Min(value, nextValue), - Close = nextValue, - Volume = Math.Abs(nextValue - value), // Volume as the change in value - Ticks = slice.Ticks - }; - } - - private HistoryItemBar GenerateFrequencySweep(DateTime time, TimeSpan slice) - { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double sweepPeriod = 48.0; // 48-hour period - - // Starting frequency (very low) - double minFreq = Math.PI / 48.0; - - // Calculate the ending frequency to ensure continuity - double maxFreq = Math.PI * 1.0 * Math.Exp(2 * Math.PI / sweepPeriod); - - // Calculate the exponential factor for frequency sweep - double expFactor = Math.Log(maxFreq / minFreq) / sweepPeriod; - - // Calculate the overall phase up to the current time - double totalPhase = (minFreq / expFactor) * (Math.Exp(expFactor * (hours % sweepPeriod)) - 1); - - // Shift the phase to start the cycle at 100 (cosine-like behavior) - totalPhase += Math.PI / 2; - - // Calculate the value of the signal at the current time - double value = 100.0 * Math.Sin(totalPhase); - - // Calculate the value of the signal at the end of the slice - double nextPhase = (minFreq / expFactor) * (Math.Exp(expFactor * ((hours + slice.TotalHours) % sweepPeriod)) - 1); - nextPhase += Math.PI / 2; // Apply the same phase shift - double nextValue = 100.0 * Math.Sin(nextPhase); - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - High = Math.Max(value, nextValue), - Low = Math.Min(value, nextValue), - Close = nextValue, - Volume = Math.Abs(nextValue - value), // Volume as the change in value - Ticks = slice.Ticks - }; - } - - - Random random = new Random(); - private double currentAmplitude = 100; - private HistoryItemBar GenerateAMSignal(DateTime time, TimeSpan slice) - { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double period = 12.0; - double frequency = 2 * Math.PI / period; // Frequency for a 5-hour period - - // Determine the start of the current 5-hour cycle - double cycleStartTime = Math.Floor(hours / period) * period; - - // Calculate the phase of the signal within the current 5-hour cycle - double phase = frequency * (hours % period); - - // If we're at the start of a new 5-hour cycle, generate a new amplitude - if (hours % period == 0) - { - currentAmplitude = random.NextDouble() * 100; - } - - // Calculate the value of the signal at the current time - double value = currentAmplitude * Math.Sin(phase); - - // Calculate the value of the signal at the end of the slice - double nextPhase = frequency * ((hours + slice.TotalHours) % period); - double nextValue = currentAmplitude * Math.Sin(nextPhase); - - // Create the HistoryItemBar - var historyItem = new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, - High = Math.Max(value, nextValue), - Low = Math.Min(value, nextValue), - Close = nextValue, // Set Close to the newly calculated value - Volume = Math.Abs(nextValue), // Volume as the change in value - Ticks = slice.Ticks - }; - - return historyItem; - } - - - private double currentFrequency = Math.PI / 220.0; // Initial frequency - private double accumulatedPhase = 0; - private double lastCloseValue = 0; // To store the last close value - - private HistoryItemBar GenerateFMSignal(DateTime time, TimeSpan slice) - { - double amplitude = 100.0; // Maximum amplitude - double minFreq = Math.PI / 256.0; - double maxFreq = Math.PI / 32.0; - - // Randomly adjust the frequency - double frequencyStep = (maxFreq - minFreq) * 0.2; // 20% of the frequency range - currentFrequency += (random.NextDouble() - 0.5) * 2 * frequencyStep; - currentFrequency = Math.Max(minFreq, Math.Min(maxFreq, currentFrequency)); // Clamp frequency - - // Calculate phase increment for this slice - double phaseIncrement = currentFrequency * slice.TotalHours; - - // Calculate the open value (which is the last close value) - double openValue = lastCloseValue; - - // Calculate the close value - accumulatedPhase += phaseIncrement; - double closeValue = amplitude * Math.Sin(2 * Math.PI * accumulatedPhase); - - // Determine high and low values - double midPhase = accumulatedPhase - (phaseIncrement / 2); - double midValue = amplitude * Math.Sin(2 * Math.PI * midPhase); - double highValue = Math.Max(Math.Max(openValue, closeValue), midValue); - double lowValue = Math.Min(Math.Min(openValue, closeValue), midValue); - - // Store the close value for the next iteration - lastCloseValue = closeValue; - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = openValue, - High = highValue, - Low = lowValue, - Close = closeValue, - Volume = Math.Abs(closeValue - openValue), // Volume as the change in value - Ticks = slice.Ticks - }; - } - - - private HistoryItemBar GenerateWhiteNoise(DateTime time, TimeSpan slice) - { - double volatility = 2; - double meanReversionStrength = 0.1; - - double openNoise = random.NextDouble(); - double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose); - double closeNoise = random.NextDouble(); - double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open); - - // Determine High and Low - double high = Math.Max(open, close); - double low = Math.Min(open, close); - - // Add variation to High and Low - double highNoise = Math.Abs(random.NextDouble()); - high += volatility * highNoise; - - double lowNoise = Math.Abs(random.NextDouble()); - low -= volatility * lowNoise; - - double volume = Math.Abs(random.NextDouble()) * 1000 + 100; - - previousClose = close; - - - // Create the HistoryItemBar - var historyItem = new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = open, - High = high, - Low = low, - Close = close, - Volume = volume, - Ticks = slice.Ticks - }; - - return historyItem; - } - - - -private double previousClose = 50; -private const double meanPrice = 50; - -private HistoryItemBar GeneratePinkNoise(DateTime time, TimeSpan slice) -{ - double volatility = 2; - double meanReversionStrength = 0.1; - - // Generate open price - double openNoise = GeneratePinkNoiseValue(); - double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose); - - // Generate close price - double closeNoise = GeneratePinkNoiseValue(); - double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open); - - // Determine High and Low - double high = Math.Max(open, close); - double low = Math.Min(open, close); - - // Add variation to High and Low - double highNoise = Math.Abs(GeneratePinkNoiseValue()); - high += volatility * highNoise; - - double lowNoise = Math.Abs(GeneratePinkNoiseValue()); - low -= volatility * lowNoise; - - double volume = Math.Abs(GeneratePinkNoiseValue()) * 1000 + 100; - - // Update previous close for the next iteration - previousClose = close; - - return new HistoryItemBar + private HistoryItemBar GenerateFMSignal(DateTime time, TimeSpan slice) { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = open, - High = high, - Low = low, - Close = close, - Volume = volume, - Ticks = slice.Ticks - }; -} + double amplitude = 100.0; // Maximum amplitude + double minFreq = Math.PI / 256.0; + double maxFreq = Math.PI / 32.0; + + // Randomly adjust the frequency + double frequencyStep = (maxFreq - minFreq) * 0.2; // 20% of the frequency range + currentFrequency += (random.NextDouble() - 0.5) * 2 * frequencyStep; + currentFrequency = Math.Max(minFreq, Math.Min(maxFreq, currentFrequency)); // Clamp frequency + + // Calculate phase increment for this slice + double phaseIncrement = currentFrequency * slice.TotalHours; + + // Calculate the open value (which is the last close value) + double openValue = lastCloseValue; + + // Calculate the close value + accumulatedPhase += phaseIncrement; + double closeValue = amplitude * Math.Sin(2 * Math.PI * accumulatedPhase); + + // Determine high and low values + double midPhase = accumulatedPhase - (phaseIncrement / 2); + double midValue = amplitude * Math.Sin(2 * Math.PI * midPhase); + double highValue = Math.Max(Math.Max(openValue, closeValue), midValue); + double lowValue = Math.Min(Math.Min(openValue, closeValue), midValue); + + // Store the close value for the next iteration + lastCloseValue = closeValue; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = openValue, + High = highValue, + Low = lowValue, + Close = closeValue, + Volume = Math.Abs(closeValue - openValue), // Volume as the change in value + Ticks = slice.Ticks + }; + } + + + private HistoryItemBar GenerateWhiteNoise(DateTime time, TimeSpan slice) + { + double volatility = 2; + double meanReversionStrength = 0.1; + + double openNoise = random.NextDouble(); + double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose); + double closeNoise = random.NextDouble(); + double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open); + + // Determine High and Low + double high = Math.Max(open, close); + double low = Math.Min(open, close); + + // Add variation to High and Low + double highNoise = Math.Abs(random.NextDouble()); + high += volatility * highNoise; + + double lowNoise = Math.Abs(random.NextDouble()); + low -= volatility * lowNoise; + + double volume = Math.Abs(random.NextDouble()) * 1000 + 100; + + previousClose = close; + + + // Create the HistoryItemBar + var historyItem = new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + + return historyItem; + } + + + + private double previousClose = 50; + private const double meanPrice = 50; + + private HistoryItemBar GeneratePinkNoise(DateTime time, TimeSpan slice) + { + double volatility = 2; + double meanReversionStrength = 0.1; + + // Generate open price + double openNoise = GeneratePinkNoiseValue(); + double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose); + + // Generate close price + double closeNoise = GeneratePinkNoiseValue(); + double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open); + + // Determine High and Low + double high = Math.Max(open, close); + double low = Math.Min(open, close); + + // Add variation to High and Low + double highNoise = Math.Abs(GeneratePinkNoiseValue()); + high += volatility * highNoise; + + double lowNoise = Math.Abs(GeneratePinkNoiseValue()); + low -= volatility * lowNoise; + + double volume = Math.Abs(GeneratePinkNoiseValue()) * 1000 + 100; + + // Update previous close for the next iteration + previousClose = close; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + } private const int NumOctaves = 6; - private double[] pinkNoiseState = new double[NumOctaves]; + private readonly double[] pinkNoiseState = new double[NumOctaves]; private double GeneratePinkNoiseValue() { double total = 0; @@ -980,154 +978,153 @@ private HistoryItemBar GeneratePinkNoise(DateTime time, TimeSpan slice) -private double lastValue = 0; + private double lastValue = 0; -private HistoryItemBar GenerateBrownNoise(DateTime time, TimeSpan slice) -{ - double dt = slice.TotalDays / 365.0; // Time step in years - double sigma = 25.0; // Annual volatility - - double increment = GenerateGaussian(0, sigma * Math.Sqrt(dt)); - double open = lastValue * (1 + GenerateGaussian(0, 0.05)); - double close = open + increment; - - // Simulate intra-period high and low - double high = Math.Max(open, close); - high += high * Math.Abs(GenerateGaussian(0, 0.06)); - double low = Math.Min(open, close); - low -= low * Math.Abs(GenerateGaussian(0, 0.06)); - - lastValue = close; - - return new HistoryItemBar + private HistoryItemBar GenerateBrownNoise(DateTime time, TimeSpan slice) { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = open, - High = high, - Low = low, - Close = close, - Volume = Math.Abs(close - open) * 1000, // Simplified volume calculation - Ticks = slice.Ticks - }; -} -// Helper method to generate Gaussian distributed random numbers -private double GenerateGaussian(double mean, double stdDev) -{ - double u1 = 1.0 - random.NextDouble(); // Uniform(0,1] random doubles - double u2 = 1.0 - random.NextDouble(); - double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); - return mean + stdDev * randStdNormal; -} + double dt = slice.TotalDays / 365.0; // Time step in years + double sigma = 25.0; // Annual volatility + double increment = GenerateGaussian(0, sigma * Math.Sqrt(dt)); + double open = lastValue * (1 + GenerateGaussian(0, 0.05)); + double close = open + increment; + // Simulate intra-period high and low + double high = Math.Max(open, close); + high += high * Math.Abs(GenerateGaussian(0, 0.06)); + double low = Math.Min(open, close); + low -= low * Math.Abs(GenerateGaussian(0, 0.06)); -private double GBMLastClose = 100; // Starting price -private double GBMMu = 0.05; // Annual drift -private double GBMSigma = 0.2; // Annual volatility + lastValue = close; -private HistoryItemBar GenerateGBM(DateTime time, TimeSpan slice) -{ - // Convert time slice to years - double dt = slice.TotalDays / 365.0; - - // Generate a random normal variable for the main price movement - double epsilon = GenerateGaussian(0, 1); - - // Calculate the price movement using GBM equation - double drift = (GBMMu - 0.5 * GBMSigma * GBMSigma) * dt; - double diffusion = GBMSigma * Math.Sqrt(dt) * epsilon; - double returnValue = Math.Exp(drift + diffusion); - - // Add variability between previous close and current open - double openVariability = GBMLastClose * GBMSigma * Math.Sqrt(dt) * GenerateGaussian(0, 1) * 0.1; - double open = GBMLastClose + openVariability; - - // Calculate new close price - double close = open * returnValue; - - // Generate High and Low values - double highLowRange = Math.Max(Math.Abs(close - open), GBMLastClose * GBMSigma * Math.Sqrt(dt) * Math.Abs(GenerateGaussian(0, 1))); - double high = Math.Max(open, close) + highLowRange * 0.5; - double low = Math.Min(open, close) - highLowRange * 0.5; - - // Generate volume (you may want to adjust this based on your needs) - double volume = Math.Max(100, 1000 * Math.Abs(close - open) + 500 * GenerateGaussian(0, 1)); - - // Update last close for next iteration - GBMLastClose = close; - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = open, - High = high, - Low = low, - Close = close, - Volume = volume, - Ticks = slice.Ticks - }; -} - - private double FBMLastClose = 100; // Starting price - private double FBMHurst = 0.85; // Hurst parameter (0.5 < H < 1 for persistent fBm) - private double FBMSigma = 0.25; // Volatility parameter - private double FBMDrift = 0.001; // drift - - private HistoryItemBar GenerateFBM(DateTime time, TimeSpan slice) + return new HistoryItemBar { - double dt = Math.Pow(slice.TotalDays / 365.0, 0.5); - - double epsilon = GenerateFractionalGaussianNoise(FBMHurst); - - double drift = FBMDrift * dt; - double diffusion = FBMSigma * Math.Pow(dt, FBMHurst) * epsilon; - - double openVariability = FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * GenerateFractionalGaussianNoise(FBMHurst) * 0.1; - double open = FBMLastClose + openVariability; - - double close = open * Math.Exp(drift + diffusion); - - double highLowRange = Math.Max(Math.Abs(close - open), - FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst)) * 2); - double high = Math.Max(open, close) + highLowRange * 0.5; - double low = Math.Min(open, close) - highLowRange * 0.5; - - double volume = Math.Max(100, 2000 * Math.Abs(close - open) + - 1000 * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst))); - - FBMLastClose = close; - - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = open, - High = high, - Low = low, - Close = close, - Volume = volume, - Ticks = slice.Ticks - }; - } - - private double GenerateFractionalGaussianNoise(double hurst) - { - double sum = 0; - int n = 1000; // Number of terms in the approximation - - for (int i = 1; i <= n; i++) - { - double ri = GenerateGaussian(0, 1); - sum += (Math.Pow(i, hurst - 0.5) - Math.Pow(i - 1, hurst - 0.5)) * ri; - } - - return sum / Math.Sqrt(n); - } - - - - // Add other necessary overrides and implementations as needed + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = Math.Abs(close - open) * 1000, // Simplified volume calculation + Ticks = slice.Ticks + }; } -} \ No newline at end of file + // Helper method to generate Gaussian distributed random numbers + private double GenerateGaussian(double mean, double stdDev) + { + double u1 = 1.0 - random.NextDouble(); // Uniform(0,1] random doubles + double u2 = 1.0 - random.NextDouble(); + double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + return mean + stdDev * randStdNormal; + } + + + + private double GBMLastClose = 100; // Starting price + private readonly double GBMMu = 0.05; // Annual drift + private readonly double GBMSigma = 0.2; // Annual volatility + + private HistoryItemBar GenerateGBM(DateTime time, TimeSpan slice) + { + // Convert time slice to years + double dt = slice.TotalDays / 365.0; + + // Generate a random normal variable for the main price movement + double epsilon = GenerateGaussian(0, 1); + + // Calculate the price movement using GBM equation + double drift = (GBMMu - 0.5 * GBMSigma * GBMSigma) * dt; + double diffusion = GBMSigma * Math.Sqrt(dt) * epsilon; + double returnValue = Math.Exp(drift + diffusion); + + // Add variability between previous close and current open + double openVariability = GBMLastClose * GBMSigma * Math.Sqrt(dt) * GenerateGaussian(0, 1) * 0.1; + double open = GBMLastClose + openVariability; + + // Calculate new close price + double close = open * returnValue; + + // Generate High and Low values + double highLowRange = Math.Max(Math.Abs(close - open), GBMLastClose * GBMSigma * Math.Sqrt(dt) * Math.Abs(GenerateGaussian(0, 1))); + double high = Math.Max(open, close) + highLowRange * 0.5; + double low = Math.Min(open, close) - highLowRange * 0.5; + + // Generate volume (you may want to adjust this based on your needs) + double volume = Math.Max(100, 1000 * Math.Abs(close - open) + 500 * GenerateGaussian(0, 1)); + + // Update last close for next iteration + GBMLastClose = close; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + } + + private double FBMLastClose = 100; // Starting price + private readonly double FBMHurst = 0.85; // Hurst parameter (0.5 < H < 1 for persistent fBm) + private readonly double FBMSigma = 0.25; // Volatility parameter + private readonly double FBMDrift = 0.001; // drift + + private HistoryItemBar GenerateFBM(DateTime time, TimeSpan slice) + { + double dt = Math.Pow(slice.TotalDays / 365.0, 0.5); + + double epsilon = GenerateFractionalGaussianNoise(FBMHurst); + + double drift = FBMDrift * dt; + double diffusion = FBMSigma * Math.Pow(dt, FBMHurst) * epsilon; + + double openVariability = FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * GenerateFractionalGaussianNoise(FBMHurst) * 0.1; + double open = FBMLastClose + openVariability; + + double close = open * Math.Exp(drift + diffusion); + + double highLowRange = Math.Max(Math.Abs(close - open), + FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst)) * 2); + double high = Math.Max(open, close) + highLowRange * 0.5; + double low = Math.Min(open, close) - highLowRange * 0.5; + + double volume = Math.Max(100, 2000 * Math.Abs(close - open) + + 1000 * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst))); + + FBMLastClose = close; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + } + + private double GenerateFractionalGaussianNoise(double hurst) + { + double sum = 0; + int n = 1000; // Number of terms in the approximation + + for (int i = 1; i <= n; i++) + { + double ri = GenerateGaussian(0, 1); + sum += (Math.Pow(i, hurst - 0.5) - Math.Pow(i - 1, hurst - 0.5)) * ri; + } + + return sum / Math.Sqrt(n); + } + + + + // Add other necessary overrides and implementations as needed +} diff --git a/SyntheticVendor/SyntheticVendor.csproj b/SyntheticVendor/SyntheticVendor.csproj index 6703559f..e853d674 100644 --- a/SyntheticVendor/SyntheticVendor.csproj +++ b/SyntheticVendor/SyntheticVendor.csproj @@ -1,6 +1,7 @@  Vendor + 0.0.0.0 diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 0f30adf3..18f54f05 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,35 +1,46 @@ - net8.0 QuanTAlib.Tests QuanTAlib.Tests - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all + runtime; build; native; contentfiles; analyzers + + + - - - + + ..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + - \ No newline at end of file + + + + + + + + diff --git a/Tests/test_Trady.cs b/Tests/test_Trady.cs index 95e9e9b1..10de3783 100644 --- a/Tests/test_Trady.cs +++ b/Tests/test_Trady.cs @@ -2,21 +2,26 @@ using Xunit; using Trady.Analysis.Indicator; using Trady.Core; using Trady.Core.Infrastructure; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; + +#pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781 + +namespace QuanTAlib; public class TradyTests { private readonly TBarSeries bars; private readonly GbmFeed feed; - private Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; - private int period, iterations; - private int skip; - private IEnumerable Candles; + private readonly int iterations; + private readonly int skip; + private readonly IEnumerable Candles; public TradyTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); bars = new(feed); range = 1e-9; @@ -33,31 +38,39 @@ public class TradyTests )).ToList(); } + private int GetRandomNumber(int minValue, int maxValue) + { + byte[] randomBytes = new byte[4]; + rng.GetBytes(randomBytes); + int randomInt = BitConverter.ToInt32(randomBytes, 0); + return Math.Abs(randomInt % (maxValue - minValue)) + minValue; + } + [Fact] public void SMA() { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } - var Trady = new SimpleMovingAverage(Candles, period) - .Compute() - .Select(result => new - { - Date = result.DateTime, - Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN - }) - .ToList(); + var Trady = new SimpleMovingAverage(Candles, period) + .Compute() + .Select(result => new + { + Date = result.DateTime, + Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN + }) + .ToList(); Assert.Equal(QL.Length, Trady.Count); for (int i = QL.Length - 1; i > skip; i--) { double QL_item = QL[i].Value; - double Tr_item = Trady[i].Value; + double Tr_item = Trady[i].Value; Assert.InRange(Tr_item - QL_item, -range, range); } } @@ -68,30 +81,28 @@ public class TradyTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Ema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } - var Trady = new ExponentialMovingAverage(Candles, period) - .Compute() - .Select(result => new - { - Date = result.DateTime, - Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN - }) - .ToList(); + var Trady = new ExponentialMovingAverage(Candles, period) + .Compute() + .Select(result => new + { + Date = result.DateTime, + Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN + }) + .ToList(); Assert.Equal(QL.Length, Trady.Count); - for (int i = QL.Length - 1; i > skip*2; i--) + for (int i = QL.Length - 1; i > skip * 2; i--) { double QL_item = QL[i].Value; - double Tr_item = Trady[i].Value; + double Tr_item = Trady[i].Value; Assert.InRange(Tr_item - QL_item, -range, range); } } } - - } \ No newline at end of file diff --git a/Tests/test_Tulip.cs b/Tests/test_Tulip.cs index 5b7cb558..8b495acc 100644 --- a/Tests/test_Tulip.cs +++ b/Tests/test_Tulip.cs @@ -1,23 +1,26 @@ using Xunit; using Tulip; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; + +#pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781 + +namespace QuanTAlib; public class TulipTests { - private readonly TBarSeries bars; private readonly GbmFeed feed; - private Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; - private int period, iterations; + private readonly int iterations; private readonly double[] data; private readonly double[] outdata; - private int skip; + private readonly int skip; public TulipTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); - bars = new(feed); range = 1e-9; feed.Add(10000); iterations = 3; @@ -26,12 +29,20 @@ public class TulipTests outdata = new double[data.Count()]; } + private int GetRandomNumber(int minValue, int maxValue) + { + byte[] randomBytes = new byte[4]; + rng.GetBytes(randomBytes); + int randomInt = BitConverter.ToInt32(randomBytes, 0); + return Math.Abs(randomInt % (maxValue - minValue)) + minValue; + } + [Fact] public void SMA() { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -44,7 +55,7 @@ public class TulipTests for (int i = QL.Length - 1; i > skip; i--) { double QL_item = QL[i].Value; - double TU = i skip*2; i--) //Initial Tulip Ema value is (wrongly) set to the first input value - therefore large skip + Assert.Equal(QL.Length, arrout[0].Length); + for (int i = QL.Length - 1; i > skip * 2; i--) //Initial Tulip Ema value is (wrongly) set to the first input value - therefore large skip { double QL_item = QL[i].Value; double TU = arrout[0][i]; - //Assert.InRange(TU - QL_item, -range, range); - Assert.True(Math.Abs(TU - QL_item) <= range, $"Assertion failed at index {i} for period {period}: TU = {TU}, QL_item = {QL_item}, delta = {TU-QL_item}"); - + Assert.True(Math.Abs(TU - QL_item) <= range, $"Assertion failed at index {i} for period {period}: TU = {TU}, QL_item = {QL_item}, delta = {TU - QL_item}"); } } } - - } \ No newline at end of file diff --git a/Tests/test_consistency.cs b/Tests/test_consistency.cs deleted file mode 100644 index 72d482cd..00000000 --- a/Tests/test_consistency.cs +++ /dev/null @@ -1,883 +0,0 @@ -using Xunit; -using QuanTAlib; - -public class Consistency -{ - Random rnd; - int series_len = 1000; - int corrections = 100; - - public Consistency() - { //constructor - rnd = new((int)DateTime.Now.Ticks); - } - - - [Fact] - public void CanUpdate() - { - - GbmFeed gbm = new(); - TSeries input = new(gbm.Close); - TSeries output = new(input); - - gbm.Add(10000); - - Assert.Equal(input.Count, output.Count); - for (int i = 0; i < input.Count; i++) - { - Assert.Equal(input[i].v, output[i].v); - } - } - - [Fact] - public void Alma_isNew() - { - int p = (int)rnd.Next(2, 100); - double offset = rnd.Next(); - double sigma = rnd.Next(1, 100); - Alma ma1 = new(period: p, offset: offset, sigma: sigma); - Alma ma2 = new(period: p, offset: offset, sigma: sigma); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Convolution_isNew() - { - Convolution ma1 = new(new double[] { 1.0, 2, 3, 2, 1 }); - Convolution ma2 = new(new double[] { 1.0, 2, 3, 2, 1 }); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Dema_isNew() - { - int p = (int)rnd.Next(2, 100); - Dema ma1 = new(p); - Dema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Dsma_isNew() - { - int p = (int)rnd.Next(2, 100); - Dsma ma1 = new(p); - Dsma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Dwma_isNew() - { - int p = (int)rnd.Next(2, 100); - Dwma ma1 = new(p); - Dwma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void EmaSma_isNew() - { - int p = (int)rnd.Next(2, 100); - Ema ma1 = new(p, useSma: true); - Ema ma2 = new(p, useSma: true); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Ema_isNew() - { - int p = (int)rnd.Next(2, 100); - Ema ma1 = new(p, useSma: false); - Ema ma2 = new(p, useSma: false); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Sma_isNew() - { - int p = (int)rnd.Next(2, 100); - Sma ma1 = new(p); - Sma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Epma_isNew() - { - int p = (int)rnd.Next(2, 100); - Epma ma1 = new(p); - Epma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Frama_isNew() - { - int p = (int)rnd.Next(2, 100); - Frama ma1 = new(p); - Frama ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Fwma_isNew() - { - int p = (int)rnd.Next(2, 100); - Fwma ma1 = new(p); - Fwma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Gma_isNew() - { - int p = (int)rnd.Next(2, 100); - Gma ma1 = new(p); - Gma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Hma_isNew() - { - int p = (int)rnd.Next(2, 100); - Hma ma1 = new(p); - Hma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - - [Fact] - public void Hwma_isNew() - { - int p = (int)rnd.Next(2, 100); - Hwma ma1 = new(p); - Hwma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - /* - [Fact] - public void Jma_isNew() - { - int p = (int)rnd.Next(2, 100); - Jma ma1 = new(p); - Jma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - - //Assert.Equal(ma1.Value, ma2.Value); - Assert.True(ma1.Value == ma2.Value, $"Assertion failed at p={p}, Value={item1.Value}. ma1.Value={ma1.Value}, ma2.Value={ma2.Value}"); - } - } - */ - - - [Fact] - public void Kama_isNew() - { - int p = (int)rnd.Next(2, 100); - Kama ma1 = new(p); - Kama ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Ltma_isNew() - { - int p = rnd.Next(0, 1); - Ltma ma1 = new(p); - Ltma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Mama_isNew() - { - int p = rnd.Next(0, 1); - Mama ma1 = new(p, p * 0.1); - Mama ma2 = new(p, p * 0.1); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - - Assert.True(ma1.Value == ma2.Value, $"Assertion failed for p={p}, i={i}. Expected {ma1.Value} but got {ma2.Value}."); - } - } - - [Fact] - public void Mgdi_isNew() - { - int p = (int)rnd.Next(2, 100); - Mgdi ma1 = new(p); - Mgdi ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Mma_isNew() - { - int p = (int)rnd.Next(2, 100); - Mma ma1 = new(p); - Mma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Qema_isNew() - { - int p = (int)rnd.Next(2, 100); - Qema ma1 = new(); - Qema ma2 = new(); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Rema_isNew() - { - int p = (int)rnd.Next(2, 100); - Rema ma1 = new(p); - Rema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Rma_isNew() - { - int p = (int)rnd.Next(2, 100); - Rma ma1 = new(p); - Rma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Sinema_isNew() - { - int p = (int)rnd.Next(2, 100); - Sinema ma1 = new(p); - Sinema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Smma_isNew() - { - int p = (int)rnd.Next(2, 100); - Smma ma1 = new(p); - Smma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void T3_isNew() - { - int p = (int)rnd.Next(2, 100); - T3 ma1 = new(p); - T3 ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Tema_isNew() - { - int p = (int)rnd.Next(2, 100); - Tema ma1 = new(p); - Tema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - - [Fact] - public void Trima_isNew() - { - int p = (int)rnd.Next(2, 100); - Trima ma1 = new(p); - Trima ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - - [Fact] - public void Vidya_isNew() - { - int p = (int)rnd.Next(2, 100); - Vidya ma1 = new(p); - Vidya ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Wma_isNew() - { - int p = (int)rnd.Next(2, 100); - Wma ma1 = new(p); - Wma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - [Fact] - public void Zlema_isNew() - { - int p = (int)rnd.Next(2, 100); - Zlema ma1 = new(p); - Zlema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Entropy_isNew() - { - int p = (int)rnd.Next(2, 100); - Entropy ma1 = new(p); - Entropy ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Kurtosis_isNew() - { - int p = (int)rnd.Next(2, 100); - Kurtosis ma1 = new(p); - Kurtosis ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Max_isNew() - { - int p = (int)rnd.Next(2, 100); - Max ma1 = new(p, 0.01); - Max ma2 = new(p, 0.01); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Min_isNew() - { - int p = (int)rnd.Next(2, 100); - Min ma1 = new(p, 0.01); - Min ma2 = new(p, 0.01); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Med_isNew() - { - int p = (int)rnd.Next(2, 100); - Median ma1 = new(p); - Median ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Mode_isNew() - { - int p = (int)rnd.Next(2, 100); - Mode ma1 = new(p); - Mode ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Percentile_isNew() - { - int p = (int)rnd.Next(2, 100); - Percentile ma1 = new(p, 50); - Percentile ma2 = new(p, 50); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Skew_isNew() - { - int p = (int)rnd.Next(2, 100); - Skew ma1 = new(p); - Skew ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Stddev_isNew() - { - int p = (int)rnd.Next(2, 100); - Stddev ma1 = new(p); - Stddev ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Variance_isNew() - { - int p = (int)rnd.Next(2, 100); - Variance ma1 = new(p); - Variance ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Zscore_isNew() - { - int p = (int)rnd.Next(2, 100); - Zscore ma1 = new(p); - Zscore ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - -} diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs new file mode 100644 index 00000000..9a61defd --- /dev/null +++ b/Tests/test_eventing.cs @@ -0,0 +1,102 @@ +using Xunit; +using System.Security.Cryptography; + +#pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781 + +namespace QuanTAlib; + +public class EventingTests +{ + [Fact] + public void EventBasedCalculations() + { + // Create a cryptographically secure random number generator + using var rng = RandomNumberGenerator.Create(); + + // Create an input series to hold our random values + var input = new TSeries(); + int p = 10; + + // Create a list of indicator pairs (direct calculation and event-based) with names + var indicators = new List<(string Name, AbstractBase Direct, AbstractBase EventBased)> + { + ("Afirma", new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)), + ("Alma", new Alma(p), new Alma(input, p)), + ("Convolution", new Convolution(new double[] {1,2,3,2,1}), new Convolution(input, new double[] {1,2,3,2,1})), + ("Dema", new Dema(p), new Dema(input, p)), + ("Dsma", new Dsma(p), new Dsma(input, p)), + ("Dwma", new Dwma(p), new Dwma(input, p)), + ("Ema", new Ema(p), new Ema(input, p)), + ("Epma", new Epma(p), new Epma(input, p)), + ("Pwma", new Pwma(p), new Pwma(input, p)), + ("Frama", new Frama(p), new Frama(input, p)), + ("Fwma", new Fwma(p), new Fwma(input, p)), + ("Gma", new Gma(p), new Gma(input, p)), + ("Hma", new Hma(p), new Hma(input, p)), + ("Htit", new Htit(), new Htit(input)), + ("Hwma", new Hwma(p), new Hwma(input, p)), + ("Jma", new Jma(p), new Jma(input, p)), + ("Kama", new Kama(p), new Kama(input, p)), + ("Ltma", new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)), + ("Maaf", new Maaf(p), new Maaf(input, p)), + ("Mama", new Mama(p), new Mama(input, p)), + ("Mgdi", new Mgdi(p, kFactor: 0.6), new Mgdi(input, p, kFactor: 0.6)), + ("Mma", new Mma(p), new Mma(input, p)), + ("Qema", 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)), + ("Rema", new Rema(p), new Rema(input, p)), + ("Rma", new Rma(p), new Rma(input, p)), + ("Sma", new Sma(p), new Sma(input, p)), + ("Wma", new Wma(p), new Wma(input, p)), + ("Rma", new Rma(p), new Rma(input, p)), + ("Tema", new Tema(p), new Tema(input, p)), + ("Kama", new Kama(2, 30, 6), new Kama(input, 2, 30, 6)), + ("Zlema", new Zlema(p), new Zlema(input, p)), + // error classes + ("Mae", new Mae(p), new Mae(input, p)), + ("Mapd", new Mapd(p), new Mapd(input, p)), + ("Mape", new Mape(p), new Mape(input, p)), + ("Mase", new Mase(p), new Mase(input, p)), + ("Mda", new Mda(p), new Mda(input, p)), + ("Me", new Me(p), new Me(input, p)), + ("Mpe", new Mpe(p), new Mpe(input, p)), + ("Mse", new Mse(p), new Mse(input, p)), + ("Msle", new Msle(p), new Msle(input, p)), + ("Rae", new Rae(p), new Rae(input, p)), + ("Rmse", new Rmse(p), new Rmse(input, p)), + ("Rmsle", new Rmsle(p), new Rmsle(input, p)), + ("Rse", new Rse(p), new Rse(input, p)), + ("Smape", new Smape(p), new Smape(input, p)), + ("Rsquared", new Rsquared(p), new Rsquared(input, p)), + ("Huberloss", new Huberloss(p), new Huberloss(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 = GetRandomDouble(rng) * 100; + input.Add(randomValue); + + // Calculate direct indicators + foreach (var (_, direct, _) in indicators) + { + direct.Calc(randomValue); + } + } + + // Compare the results of direct and event-based calculations + for (int i = 0; i < indicators.Count; i++) + { + var (name, direct, eventBased) = indicators[i]; + bool areEqual = (double.IsNaN(direct.Value) && double.IsNaN(eventBased.Value)) || + Math.Abs(direct.Value - eventBased.Value) < 1e-9; + Assert.True(areEqual, $"Indicator {name} failed: Expected {direct.Value}, Actual {eventBased.Value}"); + } + } + + private static double GetRandomDouble(RandomNumberGenerator rng) + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue; + } +} diff --git a/Tests/test_iTBar.cs b/Tests/test_iTBar.cs new file mode 100644 index 00000000..59796179 --- /dev/null +++ b/Tests/test_iTBar.cs @@ -0,0 +1,170 @@ +using Xunit; +using System.Reflection; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; + +namespace QuanTAlib; + +/// +/// Contains unit tests for bar-based indicators in QuanTAlib. +/// +public class BarIndicatorTests +{ + private readonly RandomNumberGenerator rng; + private const int SeriesLen = 1000; + private const int Corrections = 100; + + /// + /// Initializes a new instance of the BarIndicatorTests class. + /// + public BarIndicatorTests() + { + rng = RandomNumberGenerator.Create(); + } + + private static readonly ITValue[] indicators = new ITValue[] + { + new Atr(period: 14), + + // Add other TBar-based indicators here + }; + + /// + /// Tests if the indicator produces consistent results when processing new and updated bars. + /// + /// The indicator to test. + [Theory] + [MemberData(nameof(GetIndicators))] + public void IndicatorIsNew(ITValue indicator) + { + var indicator1 = indicator; + var indicator2 = indicator; + + MethodInfo calcMethod = FindCalcMethod(indicator.GetType()); + if (calcMethod == null) + { + throw new InvalidOperationException($"Calc method not found for indicator type: {indicator.GetType().Name}"); + } + + for (int i = 0; i < SeriesLen; i++) + { + TBar item1 = GenerateRandomBar(isNew: true); + InvokeCalc(indicator1, calcMethod, item1); + + for (int j = 0; j < Corrections; j++) + { + 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); + InvokeCalc(indicator2, calcMethod, item2); + + Assert.Equal(indicator1.Value, indicator2.Value); + } + } + + /// + /// Finds the appropriate Calc method for the given indicator type. + /// + /// The type of the indicator. + /// The MethodInfo for the Calc method. + 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.Find(m => + { + var parameters = m.GetParameters(); + return parameters.Length == 1 && parameters[0].ParameterType == typeof(TBar); + }); + + // If not found, return the first method + return method ?? methods[0]; + } + + type = type.BaseType!; + } + return null!; + } + + /// + /// Invokes the Calc method on the given indicator with the provided input. + /// + /// The indicator instance. + /// The Calc method to invoke. + /// The input TBar. + 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}"); + } + } + + /// + /// Generates a random TBar for testing purposes. + /// + /// Indicates whether the generated bar should be marked as new. + /// A randomly generated TBar. + private TBar GenerateRandomBar(bool isNew) + { + double open = GetRandomDouble() * 200 - 100; + double close = GetRandomDouble() * 200 - 100; + double high = Math.Max(open, close) + GetRandomDouble() * 10; + double low = Math.Min(open, close) - GetRandomDouble() * 10; + long volume = GetRandomNumber(0, 10000); + + return new TBar(Time: DateTime.Now, Open: open, High: high, Low: low, Close: close, Volume: volume, IsNew: isNew); + } + + /// + /// Generates a random double between 0 and 1. + /// + /// A random double between 0 and 1. + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue; + } + + /// + /// Generates a random integer between minValue (inclusive) and maxValue (exclusive). + /// + /// The minimum value (inclusive). + /// The maximum value (exclusive). + /// A random integer between minValue and maxValue. + private int GetRandomNumber(int minValue, int maxValue) + { + byte[] randomBytes = new byte[4]; + rng.GetBytes(randomBytes); + int randomInt = BitConverter.ToInt32(randomBytes, 0); + return Math.Abs(randomInt % (maxValue - minValue)) + minValue; + } + + /// + /// Provides the list of indicators for parameterized tests. + /// + /// An enumerable of object arrays, each containing an indicator instance. + public static IEnumerable GetIndicators() + { + return indicators.Select(indicator => new object[] { indicator }); + } +} diff --git a/Tests/test_quantower.cs b/Tests/test_quantower.cs new file mode 100644 index 00000000..23430830 --- /dev/null +++ b/Tests/test_quantower.cs @@ -0,0 +1,103 @@ +extern alias volatility; +extern alias averages; +extern alias statistics; + +using Xunit; +using System.Reflection; +using TradingPlatform.BusinessLayer; +using statistics::QuanTAlib; +using averages::QuanTAlib; +using volatility::QuanTAlib; + +namespace QuanTAlib +{ + public class QuantowerTests + { + private static void TestIndicator(string fieldName = "ma") where T : Indicator, new() + { + var indicator = new T(); + try + { + var onInitMethod = typeof(T).GetMethod("OnInit", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(onInitMethod); + onInitMethod.Invoke(indicator, null); + var onUpdateMethod = typeof(T).GetMethod("OnUpdate", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(onUpdateMethod); + + var field = typeof(T).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(field); + var fieldValue = field.GetValue(indicator); + Assert.NotNull(fieldValue); + + Assert.NotNull(indicator.ShortName); + Assert.NotEmpty(indicator.ShortName); + Assert.NotNull(indicator.Name); + Assert.NotEmpty(indicator.Name); + Assert.NotNull(indicator.Description); + Assert.NotEmpty(indicator.Description); + Assert.IsAssignableFrom(indicator); + } + catch (Exception ex) + { + throw new Xunit.Sdk.XunitException($"Test failed for {typeof(T).Name}: {ex.Message}"); + } + } + + // Averages Indicators + [Fact] public void Afirma() => TestIndicator(); + [Fact] public void Alma() => TestIndicator(); + [Fact] public void Dema() => TestIndicator(); + [Fact] public void Dsma() => TestIndicator(); + [Fact] public void Dwma() => TestIndicator(); + [Fact] public void Ema() => TestIndicator(); + [Fact] public void Epma() => TestIndicator(); + [Fact] public void Frama() => TestIndicator(); + [Fact] public void Fwma() => TestIndicator(); + [Fact] public void Gma() => TestIndicator(); + [Fact] public void Hma() => TestIndicator(); + [Fact] public void Htit() => TestIndicator(); + [Fact] public void Hwma() => TestIndicator(); + [Fact] public void Jma() => TestIndicator(); + [Fact] public void Kama() => TestIndicator(); + [Fact] public void Ltma() => TestIndicator(); + [Fact] public void Maaf() => TestIndicator(); + [Fact] public void Mama() => TestIndicator(); + [Fact] public void Mgdi() => TestIndicator(); + [Fact] public void Mma() => TestIndicator(); + [Fact] public void Pwma() => TestIndicator(); + [Fact] public void Qema() => TestIndicator(); + [Fact] public void Rema() => TestIndicator(); + [Fact] public void Rma() => TestIndicator(); + [Fact] public void Sinema() => TestIndicator(); + [Fact] public void Sma() => TestIndicator(); + [Fact] public void Smma() => TestIndicator(); + [Fact] public void T3() => TestIndicator(); + [Fact] public void Tema() => TestIndicator(); + [Fact] public void Trima() => TestIndicator(); + [Fact] public void Vidya() => TestIndicator(); + [Fact] public void Wma() => TestIndicator(); + [Fact] public void Zlema() => TestIndicator(); + + // Statistics Indicators + [Fact] public void Curvature() => TestIndicator("curvature"); + [Fact] public void Entropy() => TestIndicator("entropy"); + [Fact] public void Kurtosis() => TestIndicator("kurtosis"); + [Fact] public void Max() => TestIndicator("ma"); + [Fact] public void Median() => TestIndicator("med"); + [Fact] public void Min() => TestIndicator("mi"); + [Fact] public void Mode() => TestIndicator("mode"); + [Fact] public void Percentile() => TestIndicator("percentile"); + [Fact] public void Skew() => TestIndicator("skew"); + [Fact] public void Slope() => TestIndicator("slope"); + [Fact] public void Stddev() => TestIndicator("stddev"); + [Fact] public void Variance() => TestIndicator("variance"); + [Fact] public void Zscore() => TestIndicator("zScore"); + + // Volatility Indicators + [Fact] public void Atr() => TestIndicator("atr"); + + [Fact] public void Historical() => TestIndicator("historical"); + [Fact] public void Realized() => TestIndicator("realized"); + [Fact] public void Rvi() => TestIndicator("rvi"); + } +} diff --git a/Tests/test_skender.stock.cs b/Tests/test_skender.stock.cs index a0932094..38a87f20 100644 --- a/Tests/test_skender.stock.cs +++ b/Tests/test_skender.stock.cs @@ -1,25 +1,29 @@ using Xunit; using Skender.Stock.Indicators; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; + +#pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781 + +namespace QuanTAlib.Tests; public class SkenderTests { private readonly TBarSeries bars; private readonly GbmFeed feed; - private Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; - private int period, iterations; + private int period; + private readonly int iterations = 3; // Initialized directly at declaration private readonly IEnumerable quotes; - public SkenderTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); bars = new(feed); range = 1e-9; feed.Add(10000); - iterations = 3; quotes = bars.Select(q => new Quote { Date = q.Time, @@ -31,12 +35,20 @@ public class SkenderTests }); } + private int GetRandomNumber(int minValue, int maxValue) + { + byte[] randomBytes = new byte[4]; + rng.GetBytes(randomBytes); + int randomInt = BitConverter.ToInt32(randomBytes, 0); + return Math.Abs(randomInt % (maxValue - minValue)) + minValue; + } + [Fact] public void SMA() { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -55,7 +67,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Ema ma = new(period, useSma: true); TSeries QL = new(); foreach (TBar item in feed) @@ -74,7 +86,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Ema ma = new(period, useSma: false); TSeries QL = new(); foreach (TBar item in feed) @@ -93,7 +105,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Dema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -112,7 +124,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Tema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -131,7 +143,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); double[] kernel = Enumerable.Repeat(1.0, period).ToArray(); Convolution ma = new(kernel); TSeries QL = new(); @@ -151,7 +163,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Wma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -170,7 +182,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Hma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -189,7 +201,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Epma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -208,7 +220,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Alma ma = new(period, offset: 0.85, sigma: 6); TSeries QL = new(); foreach (TBar item in feed) @@ -227,7 +239,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); T3 ma = new(period, vfactor: 0.7, useSma: false); TSeries QL = new(); foreach (TBar item in feed) @@ -246,7 +258,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Smma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -265,7 +277,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Kama ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -284,7 +296,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) @@ -305,7 +316,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Mgdi ma = new(period: period); TSeries QL = new(); foreach (TBar item in feed) @@ -321,4 +332,23 @@ public class SkenderTests } } -} \ No newline at end of file + [Fact] + public void ATR() + { + for (int run = 0; run < iterations; run++) + { + period = GetRandomNumber(5, 55); + Atr ma = new(period: period); + TSeries QL = new(); + foreach (TBar item in bars) { QL.Add(ma.Calc(item)); } + + var atrValues = quotes.GetAtr(lookbackPeriods: period).Select(i => i.Atr.Null2NaN()!); + const int AdditionalPeriods = 500; + + for (int i = QL.Length - 1; i > 1000 + AdditionalPeriods; i--) + { + Assert.InRange(atrValues.ElementAt(i) - QL[i].Value, -range, range); + } + } + } +} diff --git a/Tests/test_talib.cs b/Tests/test_talib.cs index 82e267c9..78d21597 100644 --- a/Tests/test_talib.cs +++ b/Tests/test_talib.cs @@ -1,29 +1,36 @@ using Xunit; using TALib; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; + +namespace QuanTAlib; public class TAlibTests { - private readonly TBarSeries bars; private readonly GbmFeed feed; - private Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; - private int period, iterations; + private readonly int iterations; private readonly double[] data; private readonly double[] TALIB; - public TAlibTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); - bars = new(feed); range = 1e-9; feed.Add(10000); iterations = 3; data = feed.Close.v.ToArray(); TALIB = new double[data.Count()]; + } + private int GetRandomNumber(int minValue, int maxValue) + { + byte[] randomBytes = new byte[4]; + rng.GetBytes(randomBytes); + int randomInt = BitConverter.ToInt32(randomBytes, 0); + return Math.Abs(randomInt % (maxValue - minValue)) + minValue; } [Fact] @@ -31,7 +38,7 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -40,7 +47,6 @@ public class TAlibTests Assert.Equal(QL.Length, TALIB.Count()); for (int i = QL.Length - 1; i > period; i--) { - double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); } } @@ -51,7 +57,7 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Ema ma = new(period, useSma: true); TSeries QL = new(); foreach (TBar item in feed) @@ -60,7 +66,6 @@ public class TAlibTests Assert.Equal(QL.Length, TALIB.Count()); for (int i = QL.Length - 1; i > period; i--) { - double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); } } @@ -71,16 +76,15 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Dema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } Core.Dema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); - Assert.Equal(QL.Length, TALIB.Count()); - for (int i = QL.Length - 1; i > period*20; i--) + Assert.Equal(QL.Length, TALIB.Length); + for (int i = QL.Length - 1; i > period * 20; i--) { - double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); } } @@ -91,62 +95,36 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Tema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } Core.Tema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); - Assert.Equal(QL.Length, TALIB.Count()); - for (int i = QL.Length - 1; i > period*20; i--) + Assert.Equal(QL.Length, TALIB.Length); + for (int i = QL.Length - 1; i > period * 20; i--) { - double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); } } } -//TODO fix WMA -/* - [Fact] - public void WMA() - { - for (int run = 0; run < iterations; run++) - { - period = rnd.Next(50) + 5; - Wma ma = new(period); - TSeries QL = new(); - foreach (TBar item in feed) - { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } - Core.Wma(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); - Assert.Equal(QL.Length, TALIB.Count()); - for (int i = QL.Length - 1; i > period*3; i--) - { - double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; - Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); - } - } - } - */ - [Fact] public void T3() { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); T3 ma = new(period, vfactor: 0.7, useSma: false); TSeries QL = new(); foreach (TBar item in feed) { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } - Core.T3(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period, optInVFactor: 0.7); - Assert.Equal(QL.Length, TALIB.Count()); - for (int i = QL.Length - 1; i > period*20; i--) + Core.T3(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period, optInVFactor: 0.7); + Assert.Equal(QL.Length, TALIB.Length); + for (int i = QL.Length - 1; i > period * 20; i--) { - double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); } } } - } \ No newline at end of file diff --git a/Tests/test_updates_averages.cs b/Tests/test_updates_averages.cs new file mode 100644 index 00000000..2c06f8c5 --- /dev/null +++ b/Tests/test_updates_averages.cs @@ -0,0 +1,529 @@ +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class AveragesUpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100 + } + + [Fact] + public void Afirma_Update() + { + var indicator = new Afirma(periods: 14, taps: 4, window: Afirma.WindowType.Blackman); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Alma_Update() + { + var indicator = new Alma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Convolution_Update() + { + var indicator = new Convolution(new double[] { 1, 2, 3, 2, 1 }); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Dema_Update() + { + var indicator = new Dema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Dsma_Update() + { + var indicator = new Dsma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Dwma_Update() + { + var indicator = new Dwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Ema_Update() + { + var indicator = new Ema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Epma_Update() + { + var indicator = new Epma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Frama_Update() + { + var indicator = new Frama(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Fwma_Update() + { + var indicator = new Fwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Gma_Update() + { + var indicator = new Gma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Hma_Update() + { + var indicator = new Hma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Htit_Update() + { + var indicator = new Htit(); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Hwma_Update() + { + var indicator = new Hwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Jma_Update() + { + var indicator = new Jma(period: 14, phase: 0); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Kama_Update() + { + var indicator = new Kama(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Ltma_Update() + { + var indicator = new Ltma(gamma: 0.2); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Maaf_Update() + { + var indicator = new Maaf(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mama_Update() + { + var indicator = new Mama(fastLimit: 0.5, slowLimit: 0.05); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mgdi_Update() + { + var indicator = new Mgdi(period: 14, kFactor: 0.6); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mma_Update() + { + var indicator = new Mma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Pwma_Update() + { + var indicator = new Pwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Qema_Update() + { + var indicator = new Qema(k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rema_Update() + { + var indicator = new Rema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rma_Update() + { + var indicator = new Rma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Sinema_Update() + { + var indicator = new Sinema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Sma_Update() + { + var indicator = new Sma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Smma_Update() + { + var indicator = new Smma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void T3_Update() + { + var indicator = new T3(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Tema_Update() + { + var indicator = new Tema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Trima_Update() + { + var indicator = new Trima(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Vidya_Update() + { + var indicator = new Vidya(shortPeriod: 14, longPeriod: 30, alpha: 0.2); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Wma_Update() + { + var indicator = new Wma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Zlema_Update() + { + var indicator = new Zlema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/Tests/test_updates_errors.cs b/Tests/test_updates_errors.cs new file mode 100644 index 00000000..67db6037 --- /dev/null +++ b/Tests/test_updates_errors.cs @@ -0,0 +1,259 @@ +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class UpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100 + } + + [Fact] + public void Huberloss_Update() + { + var indicator = new Huberloss(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mae_Update() + { + var indicator = new Mae(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mapd_Update() + { + var indicator = new Mapd(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mape_Update() + { + var indicator = new Mape(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mase_Update() + { + var indicator = new Mase(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mda_Update() + { + var indicator = new Mda(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Me_Update() + { + var indicator = new Me(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mpe_Update() + { + var indicator = new Mpe(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mse_Update() + { + var indicator = new Mse(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Msle_Update() + { + var indicator = new Msle(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rae_Update() + { + var indicator = new Rae(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rmse_Update() + { + var indicator = new Rmse(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rmsle_Update() + { + var indicator = new Rmsle(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rse_Update() + { + var indicator = new Rse(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Smape_Update() + { + var indicator = new Smape(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rsquared_Update() + { + var indicator = new Rsquared(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/Tests/test_updates_statistics.cs b/Tests/test_updates_statistics.cs new file mode 100644 index 00000000..6fca5957 --- /dev/null +++ b/Tests/test_updates_statistics.cs @@ -0,0 +1,214 @@ +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class StatisticsUpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100 + } + + [Fact] + public void Curvature_Update() + { + var indicator = new Curvature(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Entropy_Update() + { + var indicator = new Entropy(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Kurtosis_Update() + { + var indicator = new Kurtosis(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Max_Update() + { + var indicator = new Max(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Median_Update() + { + var indicator = new Median(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Min_Update() + { + var indicator = new Min(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mode_Update() + { + var indicator = new Mode(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Percentile_Update() + { + var indicator = new Percentile(period: 14, percent: 50); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Skew_Update() + { + var indicator = new Skew(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Slope_Update() + { + var indicator = new Slope(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Stddev_Update() + { + var indicator = new Stddev(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Variance_Update() + { + var indicator = new Variance(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Zscore_Update() + { + var indicator = new Zscore(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/Tests/test_updates_volatility.cs b/Tests/test_updates_volatility.cs new file mode 100644 index 00000000..ba7976ee --- /dev/null +++ b/Tests/test_updates_volatility.cs @@ -0,0 +1,89 @@ +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class VolatilityUpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100 + } + + private TBar GetRandomBar(bool IsNew) + { + double open = GetRandomDouble(); + double high = open + Math.Abs(GetRandomDouble()); + double low = open - Math.Abs(GetRandomDouble()); + double close = low + (high - low) * GetRandomDouble(); + return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew); + } + + [Fact] + public void Atr_Update() + { + var indicator = new Atr(period: 14); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Historical_Update() + { + var indicator = new Historical(period: 14); + double initialValue = indicator.Calc(new TBar(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(false)); + } + double finalValue = indicator.Calc(new TBar(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Realized_Update() + { + var indicator = new Realized(period: 14); + double initialValue = indicator.Calc(new TBar(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(false)); + } + double finalValue = indicator.Calc(new TBar(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rvi_Update() + { + var indicator = new Rvi(period: 14); + double initialValue = indicator.Calc(new TBar(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(false)); + } + double finalValue = indicator.Calc(new TBar(DateTime.Now, ReferenceValue, ReferenceValue, ReferenceValue, ReferenceValue, 1000, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/archive/.editorconfig b/archive/.editorconfig deleted file mode 100644 index b99bec2f..00000000 --- a/archive/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# Top-most EditorConfig file -root = true - -[*.{cs,vb}] -# Suppress S3776 (Cognitive Complexity) -dotnet_diagnostic.S3776.severity = none -# Suppress CA1416 (Platform Compatibility) -dotnet_diagnostic.CA1416.severity = none -dotnet_style_parentheses_in_control_flow_statements = always_for_clarity:suggestion -csharp_new_line_before_open_brace = none -csharp_new_line_before_else = false -csharp_new_line_before_catch = false -csharp_new_line_before_finally = false -csharp_new_line_before_members_in_object_initializers = false -csharp_new_line_before_members_in_anonymous_types = false -csharp_new_line_between_query_expression_clauses = false \ No newline at end of file diff --git a/archive/.gitignore b/archive/.gitignore deleted file mode 100644 index f7e3d0a2..00000000 --- a/archive/.gitignore +++ /dev/null @@ -1,358 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates -.vscode/ -.fleet/ -*.deps.json -.Sandbox/ -#.sonarlint/ -.DS_Store - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ -dotCover.Output.dcvr -/Tests/GlobalSuppressions.cs diff --git a/archive/.refactoring/base.cs b/archive/.refactoring/base.cs deleted file mode 100644 index 530d38d2..00000000 --- a/archive/.refactoring/base.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; - -public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) -{ - public DateTime Time { get; init; } = Time; - public double Value { get; init; } = Value; - public bool IsNew { get; init; } = IsNew; - public bool IsHot { get; init; } = IsHot; - - public TValue() : this(DateTime.UtcNow, 0) { } - public TValue(double value) : this(DateTime.UtcNow, value) { } - public TValue((DateTime time, double value) tuple) : this(tuple.time, tuple.value) { } - - public static implicit operator double(TValue tv) => tv.Value; - public static implicit operator DateTime(TValue tv) => tv.Time; - public static implicit operator TValue(double value) => new TValue(DateTime.UtcNow, value); - - public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: {Value:F2}]"; -} - - -public readonly record struct TBar(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) -{ - public DateTime Time { get; init; } = Time; - public double Open { get; init; } = Open; - public double High { get; init; } = High; - public double Low { get; init; } = Low; - public double Close { get; init; } = Close; - public double Volume { get; init; } = Volume; - public bool IsNew { get; init; } = IsNew; - - public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { } - public TBar(double open, double high, double low, double close, double volume) : this(DateTime.UtcNow, open, high, low, close, volume) { } - public TBar((DateTime time, double open, double high, double low, double close, double volume) tuple) : this(tuple.time, tuple.open, tuple.high, tuple.low, tuple.close, tuple.volume) { } - - public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]"; -} - -///////////////////// -/// -///////////////////// - -public class GBM_Feed -{ - private readonly double _mu; - private readonly double _sigma; - private readonly Random _random; - private double _lastClose; - private double _lastHigh; - private double _lastLow; - - public GBM_Feed(double initialPrice, double mu, double sigma) - { - _lastClose = initialPrice; - _lastHigh = initialPrice; - _lastLow = initialPrice; - _mu = mu; - _sigma = sigma; - _random = Random.Shared; - } - - public TBar Generate(bool IsNew = true) - { - DateTime time = DateTime.UtcNow; - double dt = 1.0 / 252; // Assuming daily steps in a trading year of 252 days - double drift = (_mu - 0.5 * _sigma * _sigma) * dt; - double diffusion = _sigma * Math.Sqrt(dt) * NormalRandom(); - double newClose = _lastClose * Math.Exp(drift + diffusion); - - double open = _lastClose; - double high = Math.Max(open, newClose) * (1 + _random.NextDouble() * 0.01); - double low = Math.Min(open, newClose) * (1 - _random.NextDouble() * 0.01); - double volume = 1000 + _random.NextDouble() * 1000; // Random volume between 1000 and 2000 - - if (!IsNew) - { - high = Math.Max(_lastHigh, high); - low = Math.Min(_lastLow, low); - } - else - { - _lastClose = newClose; - } - - _lastHigh = high; - _lastLow = low; - - return new TBar(time, open, high, low, newClose, volume, IsNew); - } - - private double NormalRandom() - { - // Box-Muller transform to generate standard normal random variable - double u1 = 1.0 - _random.NextDouble(); // Uniform(0,1] random doubles - double u2 = 1.0 - _random.NextDouble(); - return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); - } -} - - -/// -/// //////////////// -/// - -public class EMA -{ - private double lastEma, lastEmaCandidate, k; - private int period, i; - public TValue Value { get; private set; } - public bool IsHot { get; private set; } - - public EMA(int period) { - Init(period); - } - - public void Init(int period) - { - this.period = period; - this.k = 2.0 / (period + 1); - this.lastEma = this.lastEmaCandidate = double.NaN; - this.i = 0; - } - public TValue Update(TValue input, bool IsNew = true) { - double ema; - - if (double.IsNaN(lastEma)) { lastEma = input.Value; } - - if (IsNew) { - lastEma = lastEmaCandidate; - i++; - } - - double kk = (i= period; - Value = new TValue(input.Time, ema, IsNew, IsHot); - return Value; - } -} - -///////////////// -/// - -public class SMA -{ - private CircularBuffer buffer; - private int period; - private double sum; - public TValue Value { get; private set; } - public bool IsHot { get; private set; } - - public SMA(int period) - { - Init(period); - } - - public void Init(int period) - { - this.period = period; - this.buffer = new CircularBuffer(period); - this.sum = 0; - this.IsHot = false; - this.Value = default; - } - - public TValue Update(TValue input, bool IsNew = true) - { - if (IsNew) - { - if (buffer.Count == period) { - sum -= buffer[0]; - } - buffer.Add(input); - sum += input.Value; - } else { - if (buffer.Count > 0) { - sum -= buffer[buffer.Count - 1]; - sum += input.Value; - buffer[buffer.Count - 1] = input; - } else { - buffer.Add(input); - sum += input.Value; - } - } - - double sma = buffer.Count > 0 ? sum / buffer.Count : double.NaN; - IsHot = buffer.Count >= period; - Value = new TValue(input.Time, sma, IsNew, IsHot); - return Value; - } -} - -///////////////////// -/// -///////////////////// - - -public class CircularBuffer -{ - private double[] _buffer; - private int _start; - private int _size; - - public CircularBuffer(int capacity) { - _buffer = new double[capacity]; - _start = 0; - _size = 0; - } - - public int Capacity => _buffer.Length; - public int Count => _size; - - public void Add(double item) { - if (_size < Capacity) { - _buffer[(_start + _size) % Capacity] = item; - _size++; - } else { - _buffer[_start] = item; - _start = (_start + 1) % Capacity; - } - } - - public double this[int index] { - get { - if (index < 0 || index >= _size) - throw new IndexOutOfRangeException(); - return _buffer[(_start + index) % Capacity]; - } - set { - if (index < 0 || index >= _size) - throw new IndexOutOfRangeException(); - _buffer[(_start + index) % Capacity] = value; - } - } -} \ No newline at end of file diff --git a/archive/.refactoring/test.dib b/archive/.refactoring/test.dib deleted file mode 100644 index d2277e66..00000000 --- a/archive/.refactoring/test.dib +++ /dev/null @@ -1,163 +0,0 @@ -#!meta - -{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} - -#!csharp - -#r "\bin\Debug\calculations.dll" -using QuanTAlib; - -#!csharp - -TValue vv = new(10); -display(vv.ToString()); -display(vv.IsHot); - -TBar bb = new(1,1,1,1,10); -display(bb.ToString()); -display(bb.IsNew); - -#!csharp - -int i=10; -SMA sma = new(i); -Console.WriteLine($"{"Close",10} {"SMA(" + i + ")",10}"); -for (int i = 0; i < 20; i++) -{ - TValue c =(double)i+1; - sma.Update(10000,true); - sma.Update(1,false); - sma.Update(-1000,false); - sma.Update(c,false); - - Console.WriteLine($"{i+1} {(double)c,10:F2} {(double)sma.Value,10:F2} {sma.Value.IsHot}"); -} - -#!csharp - -public class Emitter { - private Random random = new Random(); - public event EventHandler> Pub; - public void Emit() { - DateTime now = DateTime.Now; - double randomValue = random.NextDouble() * 100; // Generates a random number between 0 and 100 - TValue value = new TValue(now, randomValue); - - EventArg eventArg = new EventArg(value, true, true); - OnValuePub(eventArg); - } - protected virtual void OnValuePub(EventArg eventArg) { - Pub?.Invoke(this, eventArg); - } -} - -public class BarEmitter -{ - private Random random = new Random(); - public event EventHandler> Pub; - private double lastClose = 100.0; // Starting price - - public void Emit() - { - double open = lastClose; - double close = open * (1 + (random.NextDouble() - 0.5) * 0.02); // +/- 1% change - double high = Math.Max(open, close) * (1 + random.NextDouble() * 0.005); // Up to 0.5% higher - double low = Math.Min(open, close) * (1 - random.NextDouble() * 0.005); // Up to 0.5% lower - double volume = random.NextDouble() * 1000000; // Random volume between 0 and 1,000,000 - - TBar bar = new TBar(DateTime.Now, open, high, low, close, volume); - lastClose = close; - - EventArg eventArg = new EventArg(bar, true, true); - OnBarPub(eventArg); - } - - protected virtual void OnBarPub(EventArg eventArg) - { - Pub?.Invoke(this, eventArg); - } -} - - -public class Listener -{ - public void Sub(object sender, EventArgs e) - { - if (e is EventArg tValueArg) { - Console.WriteLine($"TValue: {tValueArg.Data.Value:F2}"); - } else if (e is EventArg tBarArg) { - Console.WriteLine($"TBar: o={tBarArg.Data.Open:F2}, v={tBarArg.Data.Volume:F2}"); - } else { - Console.WriteLine($"Unknown type: {e.GetType().Name}"); - } - } -} - -#!csharp - -Emitter em1 = new(); -BarEmitter em2 = new(); -Listener list = new(); - -em1.Pub += list.Sub; -em2.Pub += list.Sub; - -// Emit 5 random values -for (int i = 0; i < 3; i++) { - em1.Emit(); - em2.Emit(); -} - -#!csharp - -public abstract class Indicator { - protected Indicator() { - Init(); } - public virtual void Init() {} - public virtual TValue Calc(TValue input, bool isNew=true, bool isHot=true) { - return new TValue(); - } -} - -public class EMA : Indicator -{ - private double lastEma, lastEmaCandidate, k; - private int period, i; - - public EMA(int period) { - Init(period); - } - - public void Init(int period) - { - this.period = period; - this.k = 2.0 / (period + 1); - this.lastEma = this.lastEmaCandidate = double.NaN; - this.i = 0; - } - - public override TValue Calc(TValue input, bool isNew = true, bool isHot = true) { - double ema; - - if (double.IsNaN(lastEma)) { lastEma = lastEmaCandidate = input.Value; } - - if (isNew) { - lastEma = lastEmaCandidate; - i++; - } - - double kk = (i>=period)?k:(2.0/(i+1)); - ema = lastEma + kk * (input.Value - lastEma); - lastEmaCandidate = ema; - - return new TValue(input.Timestamp, ema); - } -} - -#!csharp - -EMA ema = new(3); -display(ema.Calc(100)); -display(ema.Calc(0,false)); -display(ema.Calc(100,false)); -display(ema.Calc(0)); diff --git a/archive/.sonarlint/QuanTAlib.ruleset b/archive/.sonarlint/QuanTAlib.ruleset deleted file mode 100644 index c546ccdb..00000000 --- a/archive/.sonarlint/QuanTAlib.ruleset +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/archive/.sonarlint/QuanTAlib.slconfig b/archive/.sonarlint/QuanTAlib.slconfig deleted file mode 100644 index 601908c4..00000000 --- a/archive/.sonarlint/QuanTAlib.slconfig +++ /dev/null @@ -1,19 +0,0 @@ -{ - "ServerUri": "https://sonarcloud.io/", - "Organization": { - "Key": "mihakralj", - "Name": "Miha Kralj" - }, - "ProjectKey": "mihakralj_QuanTAlib", - "ProjectName": "QuanTAlib", - "Profiles": { - "CSharp": { - "ProfileKey": "AYBIHuVX3Y_jZnooaQv1", - "ProfileTimestamp": "2022-04-20T17:57:57Z" - }, - "Secrets": { - "ProfileKey": "AYXoTKve9Ao2yLWbNVCT", - "ProfileTimestamp": "2023-01-25T09:40:14Z" - } - } -} \ No newline at end of file diff --git a/archive/.sonarlint/mihakralj_quantalib/CSharp/SonarLint.xml b/archive/.sonarlint/mihakralj_quantalib/CSharp/SonarLint.xml deleted file mode 100644 index 90bc98df..00000000 --- a/archive/.sonarlint/mihakralj_quantalib/CSharp/SonarLint.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - sonar.cs.analyzeGeneratedCode - false - - - sonar.cs.file.suffixes - .cs - - - sonar.cs.ignoreHeaderComments - true - - - sonar.cs.roslyn.ignoreIssues - false - - - - - S107 - - - max - 7 - - - - - S110 - - - max - 5 - - - - - S1479 - - - maximum - 30 - - - - - S2342 - - - flagsAttributeFormat - ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?s$ - - - format - ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$ - - - - - S2436 - - - max - 2 - - - maxMethod - 3 - - - - - S3776 - - - propertyThreshold - 3 - - - threshold - 15 - - - - - \ No newline at end of file diff --git a/archive/.sonarlint/mihakralj_quantalib_secrets_settings.json b/archive/.sonarlint/mihakralj_quantalib_secrets_settings.json deleted file mode 100644 index 1a0a4ae7..00000000 --- a/archive/.sonarlint/mihakralj_quantalib_secrets_settings.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "sonarlint.rules": { - "secrets:S6338": { - "level": "On", - "severity": "Blocker" - }, - "secrets:S6337": { - "level": "On", - "severity": "Blocker" - }, - "secrets:S6290": { - "level": "On", - "severity": "Blocker" - }, - "secrets:S6334": { - "level": "On", - "severity": "Blocker" - }, - "secrets:S6336": { - "level": "On", - "severity": "Blocker" - }, - "secrets:S6335": { - "level": "On", - "severity": "Blocker" - }, - "secrets:S6292": { - "level": "On", - "severity": "Blocker" - } - } -} \ No newline at end of file diff --git a/archive/.sonarlint/mihakralj_quantalibcsharp.ruleset b/archive/.sonarlint/mihakralj_quantalibcsharp.ruleset deleted file mode 100644 index 5ad478ec..00000000 --- a/archive/.sonarlint/mihakralj_quantalibcsharp.ruleset +++ /dev/null @@ -1,390 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/archive/.sonarlint/sonar.settings.json b/archive/.sonarlint/sonar.settings.json deleted file mode 100644 index 10d827d5..00000000 --- a/archive/.sonarlint/sonar.settings.json +++ /dev/null @@ -1 +0,0 @@ -{"sonar.exclusions":[],"sonar.global.exclusions":["**/build-wrapper-dump.json"],"sonar.inclusions":[]} \ No newline at end of file diff --git a/archive/Calculations/Basics/ADD_Series.cs b/archive/Calculations/Basics/ADD_Series.cs deleted file mode 100644 index 6d0436fe..00000000 --- a/archive/Calculations/Basics/ADD_Series.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -ADD - adding TSeries+TSeries together, or TSeries+double, or double+TSeries - -Remarks: - Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator. - - */ - -public class ADD_Series : Pair_TSeries_Indicator -{ - public ADD_Series(TSeries d1, TSeries d2) : base(d1, d2) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public ADD_Series(TSeries d1, double dd2) : base(d1, dd2) - { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public ADD_Series(double dd1, TSeries d2) : base(dd1, d2) - { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, TValue1.v + TValue2.v); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} diff --git a/archive/Calculations/Basics/CORR_Series.cs b/archive/Calculations/Basics/CORR_Series.cs deleted file mode 100644 index b5209361..00000000 --- a/archive/Calculations/Basics/CORR_Series.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -CORR: Pearson's Correlation Coefficient - PCC is a measure of linear correlation between two sets of data. - It is the ratio between the covariance of two variables and the product of - their standard deviations; it is essentially a normalized measurement of - the covariance, such that the result always has a value between −1 and 1. - -Sources: - https://en.wikipedia.org/wiki/Pearson_correlation_coefficient - - */ - -public class CORR_Series : Pair_TSeries_Indicator -{ - public CORR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - - private readonly System.Collections.Generic.List _x = new(); - private readonly System.Collections.Generic.List _xx = new(); - private readonly System.Collections.Generic.List _y = new(); - private readonly System.Collections.Generic.List _yy = new(); - private readonly System.Collections.Generic.List _xy = new(); - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - Add_Replace_Trim(_x, TValue1.v, _p, update); - Add_Replace_Trim(_xx, TValue1.v * TValue1.v, _p, update); - Add_Replace_Trim(_y, TValue2.v, _p, update); - Add_Replace_Trim(_yy, TValue2.v * TValue2.v, _p, update); - Add_Replace_Trim(_xy, TValue1.v * TValue2.v, _p, update); - - double _sumx = _x.Sum(); - double _sumxx = _xx.Sum(); - double _sumy = _y.Sum(); - double _sumyy = _yy.Sum(); - double _sumxy = _xy.Sum(); - - double _covar = (_sumxx - _sumx * _sumx / _p) * (_sumyy - _sumy * _sumy / _p); - double _cor = (_covar != 0) ? (_sumxy - _sumx * _sumy / _p) / Math.Sqrt(_covar) : 0.0; - - var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _cor); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - - } -} diff --git a/archive/Calculations/Basics/COVAR_Series.cs b/archive/Calculations/Basics/COVAR_Series.cs deleted file mode 100644 index ee700873..00000000 --- a/archive/Calculations/Basics/COVAR_Series.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -COVAR: Covariance - Covariance is defined as the expected value (or mean) of the product - of their deviations from their individual expected values. - -Sources: - https://en.wikipedia.org/wiki/Covariance - - */ - - -public class COVAR_Series : Pair_TSeries_Indicator -{ - public COVAR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN) - { - if (base._d1.Count > 0 && base._d2.Count > 0) - { - for (int i = 0; i < base._d1.Count; i++) - { - this.Add(base._d1[i], base._d2[i], false); - } - } - } - - private readonly System.Collections.Generic.List _x = new(); - private readonly System.Collections.Generic.List _y = new(); - private readonly System.Collections.Generic.List _xy = new(); - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - BufferTrim(_x, TValue1.v, _p, update); - BufferTrim(_y, TValue2.v, _p, update); - BufferTrim(_xy, TValue1.v * TValue2.v, _p, update); - - double _avgx = _x.Average(); - double _avgy = _y.Average(); - double _avgxy = _xy.Average(); - double _covar = _avgxy - (_avgx * _avgy); - - var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _covar); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} diff --git a/archive/Calculations/Basics/DIV_Series.cs b/archive/Calculations/Basics/DIV_Series.cs deleted file mode 100644 index 962e4f6f..00000000 --- a/archive/Calculations/Basics/DIV_Series.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -DIV - divide TSeries/TSeries , or TSeries/double, or double/TSeries - -Remarks: - Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator. - */ - -public class DIV_Series : Pair_TSeries_Indicator -{ - public DIV_Series(TSeries d1, TSeries d2) : base(d1, d2) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public DIV_Series(TSeries d1, double dd2) : base(d1, dd2) - { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public DIV_Series(double dd1, TSeries d2) : base(dd1, d2) - { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, - (TValue2.v is not 0) ? TValue1.v / TValue2.v : Double.PositiveInfinity); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} \ No newline at end of file diff --git a/archive/Calculations/Basics/MUL_Series.cs b/archive/Calculations/Basics/MUL_Series.cs deleted file mode 100644 index b2bae613..00000000 --- a/archive/Calculations/Basics/MUL_Series.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -MUL - multiply TSeries*TSeries together, or TSeries*double, or double*TSeries - - */ - -public class MUL_Series : Pair_TSeries_Indicator -{ - public MUL_Series(TSeries d1, TSeries d2) : base(d1, d2) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public MUL_Series(TSeries d1, double dd2) : base(d1, dd2) - { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public MUL_Series(double dd1, TSeries d2) : base(dd1, d2) - { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, - TValue1.v * TValue2.v); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} \ No newline at end of file diff --git a/archive/Calculations/Basics/SUB_Series.cs b/archive/Calculations/Basics/SUB_Series.cs deleted file mode 100644 index e4333ec6..00000000 --- a/archive/Calculations/Basics/SUB_Series.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -SUB - subtracting TSeries-TSeries, or TSeries-double, or double-TSeries - - */ - - -public class SUB_Series : Pair_TSeries_Indicator -{ - public SUB_Series(TSeries d1, TSeries d2) : base(d1, d2) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public SUB_Series(TSeries d1, double dd2) : base(d1, dd2) - { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public SUB_Series(double dd1, TSeries d2) : base(dd1, d2) - { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, - TValue1.v - TValue2.v); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} \ No newline at end of file diff --git a/archive/Calculations/Calculations.csproj b/archive/Calculations/Calculations.csproj deleted file mode 100644 index 701f67f7..00000000 --- a/archive/Calculations/Calculations.csproj +++ /dev/null @@ -1,80 +0,0 @@ - - - - QuanTAlib - 0.2.30 - 0.2.30 - 0.2.30 - Library of TA Calculations, Charts and Strategies for Quantower - Quantitative Technical Analysis Library in C# for Quantower - git - https://github.com/mihakralj/QuanTAlib - true - Miha Kralj - Miha Kralj - Apache-2.0 - readme.md - net8.0;net7.0 - disable - preview - disable - true - en-US - QuanTAlib - QuanTAlib - True - AnyCPU - False - full - True - True - - Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo; - AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex; - Quantitative;Historical;Quotes; - - - - - - - - - - full - True - 7 - True - anycpu - - - full - True - 7 - True - anycpu - - - QuanTAlib2.png - https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png - True - ..\.sonarlint\mihakralj_quantalibcsharp.ruleset - 0.2.1-dev.2 - - - - - - - True - - - - - True - False - - - - - \ No newline at end of file diff --git a/archive/Calculations/ClassStructures/Pair_TSeries_Abstract.cs b/archive/Calculations/ClassStructures/Pair_TSeries_Abstract.cs deleted file mode 100644 index 5d1dc59f..00000000 --- a/archive/Calculations/ClassStructures/Pair_TSeries_Abstract.cs +++ /dev/null @@ -1,157 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -Abstract classes with all scaffolding required to build indicators. - All abstracts support period, NaN, and all permutations of Add() methods. - Indicator classess need to implement: - - Chaining constructor (Abstract's constructor executes first) - - Default Add(value) class - - optional Add(series) bulk insert class (for optimization of historical analysis) - - Single_TSeries_Indicator - one single-value TSeries in, one TSeries out. - Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring) - Single_TBars_Indicator - One OHLCV TBars in, one TSeries out. - - */ - -public abstract class Pair_TSeries_Indicator : TSeries -{ - protected readonly int _p; - protected readonly bool _NaN; - protected readonly TSeries _d1; - protected readonly TSeries _d2; - protected readonly double _dd1, _dd2; - - // Chainable Constructors - add them at the end of primary constructors if needed - protected Pair_TSeries_Indicator(TSeries source1, TSeries source2, int period, bool useNaN) - { - _p = period; - _NaN = useNaN; - _d1 = source1; - _d2 = source2; - _dd1 = double.NaN; - _dd2 = double.NaN; - _d1.Pub += Sub; - _d2.Pub += Sub; - } - - protected Pair_TSeries_Indicator(TSeries source1, TSeries source2) - { - _d1 = source1; - _d2 = source2; - _dd1 = double.NaN; - _dd2 = double.NaN; - _d1.Pub += Sub; - _d2.Pub += Sub; - } - - protected Pair_TSeries_Indicator(TSeries source1, double dd2) - { - _d1 = source1; - _d2 = new TSeries(); - _dd1 = double.NaN; - _dd2 = dd2; - _d1.Pub += Sub; - } - - protected Pair_TSeries_Indicator(double dd1, TSeries source2) - { - _d1 = new TSeries(); - _d2 = source2; - _dd1 = dd1; - _dd2 = double.NaN; - _d2.Pub += Sub; - } - - // overridable Add(Tvalue, Tvalue) method to add/update a single value at the end of the list - public virtual void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2, bool update) - { - base.Add((TValue1.t, 0), update); - // default inserts zeros - } - - // potentially overridable Add() bulk variations (could be replaced with faster bulk algos) - public virtual void Add(TSeries d1, TSeries d2) - { - for (var i = 0; i < d1.Count; i++) - { - Add(d1[i], d2[i], false); - } - } - - public virtual void Add(TSeries d1, double dd2) - { - for (var i = 0; i < d1.Count; i++) - { - Add(d1[i], (d1[i].t, dd2), false); - } - } - - public virtual void Add(double dd1, TSeries d2) - { - for (var i = 0; i < d2.Count; i++) - { - Add((d2[i].t, dd1), d2[i], false); - } - } - - public void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2) - { - Add(TValue1, TValue2, false); - } - - public void Add(bool update) - { - if (_dd1 is double.NaN && _dd2 is double.NaN) - { - // (Series, Series) - if (update || (_d1.Count > Count && _d2.Count > Count)) - { - Add(_d1[_d1.Count - 1], _d2[_d2.Count - 1], update); - } - } - else if (_dd2 is not double.NaN && _dd1 is double.NaN) - { - // (Series, Double) - Add(_d1[_d1.Count - 1], (_d1[_d1.Count - 1].t, _dd2), update); - } - else - { - // (Double, Series) - Add((_d2[_d2.Count - 1].t, _dd1), _d2[_d2.Count - 1], update); - } - } - - public void Add() - { - Add(false); - } - - public new void Sub(object source, TSeriesEventArgs e) - { - Add(e.update); - } - - protected static void Add_Replace(List l, double v, bool update) - { - if (update) - { - l[l.Count - 1] = v; - } - else - { - l.Add(v); - } - } - - protected static void Add_Replace_Trim(List l, double v, int p, bool update) - { - Add_Replace(l, v, update); - if (l.Count > p && p != 0) - { - l.RemoveAt(0); - } - } -} diff --git a/archive/Calculations/Feeds/Alphavantage_Feed.cs b/archive/Calculations/Feeds/Alphavantage_Feed.cs deleted file mode 100644 index f2bcc60e..00000000 --- a/archive/Calculations/Feeds/Alphavantage_Feed.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Text.Json; - -/* -Alphavantage - Free API to collect 100 recent daily quotes. It requires a (free) API key - Get API key at https://www.alphavantage.co/support/#api-key - Parameters: - Symbol: stock ("AAPL"), - APIkey: unique Alphavantage API key - - -*/ -public class Alphavantage_Feed : TBars -{ - public enum Interval { Month, Week, Day, Hour, Min30, Min15, Min5, Min1 } - public Alphavantage_Feed(string Symbol = "IBM", string APIkey = "demo") - { - System.Net.Http.HttpClient client = new(); - - string req = "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED" + "&symbol=" + Symbol + "&apikey=" + APIkey; - var msg = client.GetStringAsync(req).Result; - var jres = JsonSerializer.Deserialize(msg).RootElement; - jres.TryGetProperty("Time Series (Daily)", out JsonElement json); - - if (json.ValueKind == JsonValueKind.Undefined) { throw new InvalidOperationException("Stock symbol " + Symbol + " not found"); } - foreach (var val in json.EnumerateObject()) { base.Add(GetOHLC(val)); } - base.Reverse(); - } - private static (DateTime t, double o, double h, double l, double c, double v) GetOHLC(JsonProperty json) - { - double o, h, l, c, v; - o = h = l = c = v = 0; - DateTime date = Convert.ToDateTime(json.Name); - foreach (var val in json.Value.EnumerateObject()) - { - switch (val.Name) - { - case "1. open": o = Convert.ToDouble(val.Value.ToString()); break; - case "1b. open (USD)": o = Convert.ToDouble(val.Value.ToString()); break; - case "2. high": h = Convert.ToDouble(val.Value.ToString()); break; - case "2b. high (USD)": h = Convert.ToDouble(val.Value.ToString()); break; - case "3. low": l = Convert.ToDouble(val.Value.ToString()); break; - case "3b. low (USD)": l = Convert.ToDouble(val.Value.ToString()); break; - case "4. close": c = Convert.ToDouble(val.Value.ToString()); break; - case "4b. close (USD)": c = Convert.ToDouble(val.Value.ToString()); break; - case "5. adjusted close": c = Convert.ToDouble(val.Value.ToString()); break; - case "5. volume": v = Convert.ToDouble(val.Value.ToString()); break; - case "6. volume": v = Convert.ToDouble(val.Value.ToString()); break; - default: o = 0; h = 0; l = 0; c = 0; v = 0; break; - } - } - return (date, o, h, l, c, v); - } -} \ No newline at end of file diff --git a/archive/Calculations/Feeds/GBM_Feed.cs b/archive/Calculations/Feeds/GBM_Feed.cs deleted file mode 100644 index c99a282f..00000000 --- a/archive/Calculations/Feeds/GBM_Feed.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -GBM - Geometric Brownian Motion is a random simulator of market movement, returning List - GBM can be used for testing indicators, validation and Monte Carlo simulations of strategies. - - Sample usage: - GBM-Random data = new(); // generates 1 year (252) list of bars - GBM-Random data = new(Bars: 1000); // generates 1,000 bars - GBM-Random data = new(Bars: 252, Volatility: 0.05, Drift: 0.0005, Seed: 100.0) - - Parameters - Bars: number of bars (quotes) requested - Volatility: how dymamic/volatile the series should be; default is 1 - Drift: incremental drift due to annual interest rate; default is 5% - Seed: starting value of the random series; should not be 0 - - */ - -public class GBM_Feed : TBars -{ - private double seed; - readonly double drift, volatility; - readonly int precision; - public GBM_Feed(int Bars = 252, double Volatility = 1.0, double Drift = 0.05, double Seed = 100.0, int Precision = 2) - { - this.seed = Seed; - volatility = Volatility * 0.01; - drift = Drift * 0.01; - precision = Precision; - for (int i = 0; i < Bars; i++) - { - DateTime Timestamp = DateTime.Today.AddDays(i - Bars); - this.Add(Timestamp); - } - } - - public void Add(bool update = false) { this.Add(DateTime.Now, update); } - public void Add(DateTime timestamp, bool update = false) - { - double Open = GBM_value(seed, volatility * volatility, drift, precision); - double Close = GBM_value(Open, volatility, drift, precision); - - double OCMax = Math.Max(Open, Close); - double High = (GBM_value(seed, volatility * 0.5, 0, precision)); - High = (High < OCMax) ? (2 * OCMax) - High : High; - - double OCMin = Math.Min(Open, Close); - double Low = (GBM_value(seed, volatility * 0.5, 0, precision)); - Low = (Low > OCMin) ? (2 * OCMin) - Low : Low; - - double Volume = GBM_value(seed * 10, volatility * 2, Drift: 0, precision: 1); - - base.Add((timestamp, Open, High, Low, Close, Volume), update); - seed = Close; - } - - private static double GBM_value(double Seed, double Volatility, double Drift, int precision) - { - Random rnd = new(); - double U1 = 1.0 - rnd.NextDouble(); - double U2 = 1.0 - rnd.NextDouble(); - double Z = Math.Sqrt(-2.0 * Math.Log(U1)) * Math.Sin(2.0 * Math.PI * U2); - return Math.Round(Seed * Math.Exp(Drift - (Volatility * Volatility * 0.5) + (Volatility * Z)), digits: precision); - } -} \ No newline at end of file diff --git a/archive/Calculations/Feeds/RND_Feed.cs b/archive/Calculations/Feeds/RND_Feed.cs deleted file mode 100644 index c6e311af..00000000 --- a/archive/Calculations/Feeds/RND_Feed.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -Random Bars generator - used for testing, validation and fun - Returns 'bars' number of candles that follow common market movement. - volatility defines how 'jumpy' is the series of - startvalue defines beginning closing price that then guides the rest of series - - */ - -public class RND_Feed : TBars -{ - public RND_Feed(int Bars, double Volatility = 0.05, double Startvalue = 100.0) - { - Random rnd = new(); - double c = Startvalue; - for (int i = 0; i < Bars; i++) - { - double o = Math.Round(c + (c * (((Volatility * 0.1) * rnd.NextDouble()) - 0.005)), 2); - double h = Math.Round(o + (c * Volatility * rnd.NextDouble()), 2); - double l = Math.Round(o - (c * Volatility * rnd.NextDouble()), 2); - c = Math.Round(l + ((h - l) * rnd.NextDouble()), 2); - double v = Math.Round(1000 * rnd.NextDouble(), 2); - this.Add(DateTime.Today.AddDays(i - Bars), o, h, l, c, v); - } - } -} \ No newline at end of file diff --git a/archive/Calculations/Feeds/Yahoo_Feed.cs b/archive/Calculations/Feeds/Yahoo_Feed.cs deleted file mode 100644 index 1d87e97d..00000000 --- a/archive/Calculations/Feeds/Yahoo_Feed.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Text.Json; - -/* -Yahoo Finance - Free API feed to collect daily market quotes - Parameters: - Symbol: stock symbol (default: "IBM") - Period: number of days of collected history (default: 252) - Usage: - Yahoo_Feed ticker = new("MSFT", 20) - - -*/ -public class Yahoo_Feed : TBars -{ - public Yahoo_Feed(string Symbol = "IBM", int Period = 252) - { - Period = (int)(Period * 1.45); - string requestUrl = "https://query1.finance.yahoo.com/v8/finance/chart/" + - Symbol + "?interval=1d&period1=" + - (int)new DateTimeOffset(DateTime.UtcNow.AddDays(-Period + 1)).ToUnixTimeSeconds() + "&period2=" + - (int)new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); - System.Net.Http.HttpClient client = new(); - var msg = client.GetStringAsync(requestUrl).Result; - var jresult = JsonSerializer.Deserialize(msg).RootElement; - - jresult.TryGetProperty("chart", out JsonElement json); - json.TryGetProperty("result", out json); - json[0].TryGetProperty("timestamp", out JsonElement datetime); - json[0].TryGetProperty("indicators", out json); - json.TryGetProperty("quote", out json); - json[0].TryGetProperty("open", out JsonElement open); - json[0].TryGetProperty("high", out JsonElement high); - json[0].TryGetProperty("low", out JsonElement low); - json[0].TryGetProperty("close", out JsonElement close); - json[0].TryGetProperty("volume", out JsonElement volume); - - for (int i = 0; i < datetime.GetArrayLength(); i++) - { - DateTime d = DateTimeOffset.FromUnixTimeSeconds(long.Parse(datetime[i].GetRawText())).DateTime; - double o = Math.Round(double.Parse(open[i].GetRawText()), 3); - double h = Math.Round(double.Parse(high[i].GetRawText()), 3); - double l = Math.Round(double.Parse(low[i].GetRawText()), 3); - double c = Math.Round(double.Parse(close[i].GetRawText()), 3); - double v = Math.Round(double.Parse(volume[i].GetRawText()), 3); - base.Add(d, o, h, l, c, v); - } - } -} diff --git a/archive/Calculations/Logic/COMPARE_Series.cs b/archive/Calculations/Logic/COMPARE_Series.cs deleted file mode 100644 index 70d26902..00000000 --- a/archive/Calculations/Logic/COMPARE_Series.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -COMPARE - Generates +1 if A is above B, -1 if A is below B and 0 if A=B - - - */ - -public class COMPARE_Series : Pair_TSeries_Indicator -{ - - public COMPARE_Series(TSeries d1, TSeries d2) : base(d1, d2) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public COMPARE_Series(TSeries d1, double dd2) : base(d1, dd2) - { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public COMPARE_Series(double dd1, TSeries d2) : base(dd1, d2) - { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - - double val = TValue1.v > TValue2.v ? 1 : -1; - val = TValue1.v == TValue2.v ? 0 : val; - (System.DateTime t, double v) over = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, TValue1.v > TValue2.v ? 1 : val); - if (update) { base[^1] = over; } - else { base.Add(over); } - - - } -} - - diff --git a/archive/Calculations/Logic/CROSS_Series.cs b/archive/Calculations/Logic/CROSS_Series.cs deleted file mode 100644 index b9a40d9b..00000000 --- a/archive/Calculations/Logic/CROSS_Series.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -OVER - Generates +1 if A is above B, -1 if A is below B and 0 if A=B - -Remarks: - OVER.Cross generates 1 when A breaks B from below and -1 when A breaks B from above - - */ - -public class CROSS_Series : Pair_TSeries_Indicator -{ - public TSeries Cross { get; set; } = new(); - - private double _previous = double.NaN; - public CROSS_Series(TSeries d1, TSeries d2) : base(d1, d2) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public CROSS_Series(TSeries d1, double dd2) : base(d1, dd2) - { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public CROSS_Series(double dd1, TSeries d2) : base(dd1, d2) - { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - - double val = TValue1.v > TValue2.v ? 1 : -1; - val = TValue1.v == TValue2.v ? 0 : val; - double over = TValue1.v > TValue2.v ? 1 : 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; - - if (update) { base[^1] = result; } - else { base.Add(result); } - - } -} - - diff --git a/archive/Calculations/Logic/EQUITY_Series.cs b/archive/Calculations/Logic/EQUITY_Series.cs deleted file mode 100644 index 8323feb7..00000000 --- a/archive/Calculations/Logic/EQUITY_Series.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -EQUITY - Generates P&L portfolio based on trades signals and equity prices - - */ - - -//base prices: bars.close -//trade signals: trades -//optional: long, short, long&short -//optional: warmup period: warmup - -/* - -public class EQUITY_Series : Single_TSeries_Indicator { - readonly TSeries inmarket; //for every bar - private readonly TSeries _price; - private double _equity; - private readonly double _capital; - - readonly int _warmup; - double _cash; - int _units; - private bool _longbuy, _longsell; - double _long_order, _open_order; - double _investment_value; - short _inmarket; - - public EQUITY_Series(TSeries signal, TSeries price, int warmup = 0, double capital = 1000) : base(signal, period: 0, useNaN: false) { - _capital = capital; - _cash = _capital; - _investment_value = 0; - _warmup = (warmup > 0) ? warmup : 1; - - inmarket = new(); - _longbuy = _longsell = false; - _open_order = 0; - _inmarket = 0; - _units = 0; - _long_order = 0; - - _price = price; //we buy on the Open price of the NEXT bar - _long_order = 0; - - if (base._data.Count > 0) { base.Add(base._data); } - } - - public override void Add((System.DateTime t, double v) TValue, bool update) { - - if (this.Count > _warmup) { - - // harvest the gain-loss from previous day - _investment_value = _units * _price[this.Count - 1].v; - _equity = _cash + _investment_value; - - - //execute orders from previous bar - if (_longbuy && _inmarket == 0) { //time to execute the long buy - _units = (int)(_cash / _price[this.Count - 1].v); - _long_order = _units * _price[this.Count - 1].v; - _cash -= _long_order; - _open_order = _long_order; - _equity = _cash + _open_order; - _inmarket = 1; - _longbuy = false; - } - - if (_longsell && _inmarket == 1) { //time to execute the long sell - _long_order = (_units * _price[this.Count - 1].v); - _cash += _long_order; - _units = 0; - - _open_order = 0; - _equity = _cash + _open_order; - _inmarket = 0; - _longsell = false; - } - - if (_inmarket == 0 && TValue.v == 1) { _longbuy = true; } //out of market, enter long - if (_inmarket == 1 && TValue.v == -1) { _longsell = true; } //long market, exit long - - //Console.WriteLine($"{TValue.v,3}\t {(_inmarket)} : {_cash,10:f2} + {_units*_price[^1].v,7:f2} = {_equity-_capital:f2}"); - } - inmarket.Add((TValue.t, (double)_inmarket)); - base.Add((TValue.t, _equity), update, _NaN); - } -} - -*/ \ No newline at end of file diff --git a/archive/Calculations/Logic/TOrders.cs b/archive/Calculations/Logic/TOrders.cs deleted file mode 100644 index ec681ec5..00000000 --- a/archive/Calculations/Logic/TOrders.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data; -using System.Linq; - - -public enum OType -{ - NIL = 0, // No position - BTO = 1, // Buy to Open - STC = 2, // Sell to Close - STO = 3, // Sell to Open - BTC = 4, // Buy to Close - END = 5, // Exit the trade -} - - -public class TOrders : List<(DateTime t, OType o)> -{ - - public void Add((DateTime t, OType o) TOrder, bool update = false) - { - if (update) { this[^1] = TOrder; } - else { base.Add(TOrder); } - OnEvent(update); - } - - - protected virtual void OnEvent(bool update = false) - { - Pub?.Invoke(this, new TSeriesEventArgs { update = update }); - } - public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); - public event NewDataEventHandler Pub; - -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ADL_Series.cs b/archive/Calculations/_Updated/ADL_Series.cs deleted file mode 100644 index 9873a1cc..00000000 --- a/archive/Calculations/_Updated/ADL_Series.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ADL: Chaikin Accumulation/Distribution Line - ADL is a volume-based indicator that measures the cumulative Money Flow Volume: - - 1. Money Flow Multiplier = [(Close - Low) - (High - Close)] /(High - Low) - 2. Money Flow Volume = Money Flow Multiplier x Volume for the Period - 3. ADL = Previous ADL + Current Period's Money Flow Volume - -Sources: - https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line - - */ - -public class ADL_Series : TSeries -{ - protected readonly TBars _data; - private double _lastadl, _lastlastadl; - - //core constructors - public ADL_Series() - { - Name = $"ADL()"; - _lastadl = _lastlastadl = 0; - } - public ADL_Series(TBars source) - { - _data = source; - Name = $"ADL({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _lastadl = _lastlastadl = 0; - _data.Pub += Sub; - Add(data: _data); - } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - if (update) { this._lastadl = this._lastlastadl; } - else { this._lastlastadl = this._lastadl; } - - double _adl = 0; - double tmp = TBar.h - TBar.l; - if (tmp > 0.0) - { - _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); - } - _lastadl = _adl; - - var ret = (TBar.t, _adl); - return base.Add(ret, update); - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _lastadl = _lastlastadl = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ADOSC_Series.cs b/archive/Calculations/_Updated/ADOSC_Series.cs deleted file mode 100644 index b3ebc2c1..00000000 --- a/archive/Calculations/_Updated/ADOSC_Series.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ADOSC: Chaikin Accumulation/Distribution Oscillator - ADO measures the momentum of ADL using the difference between slow (10-day) EMA(ADL) - and fast (3-day) EMA(ADL): - - Chaikin A/D Oscillator is defined as 3-day EMA of ADL minus 10-day EMA of ADL - -Sources: - https://school.stockcharts.com/doku.php?id=technical_indicators:chaikin_oscillator - - */ - -public class ADOSC_Series : TSeries -{ - protected readonly TBars _data; - private readonly double _k1, _k2; - private double _lastema1, _lastlastema1, _lastema2, _lastlastema2; - private double _lastadl, _lastlastadl; - - //core constructors - public ADOSC_Series(int shortPeriod, int longPeriod, bool useNaN = false) - { - Name = $"ADOSC()"; - _k1 = 2.0 / (shortPeriod + 1); - _k2 = 2.0 / (longPeriod + 1); - _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; - } - public ADOSC_Series(TBars source, int shortPeriod, int longPeriod, bool useNaN = false) : this(shortPeriod, longPeriod, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _lastadl = _lastlastadl = 0; - _data.Pub += Sub; - Add(data: _data); - } - - public ADOSC_Series() : this(shortPeriod: 3, longPeriod: 10, useNaN: false) { } - - public ADOSC_Series(TBars source) : this(source, shortPeriod: 3, longPeriod: 10, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - - if (update) - { - _lastadl = _lastlastadl; - _lastema1 = _lastlastema1; - _lastema2 = _lastlastema2; - } - - double _adl = 0; - double tmp = TBar.h - TBar.l; - if (tmp > 0.0) { _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); } - if (this.Count == 0) { _lastema1 = _lastema2 = _adl; } - - double _ema1 = (_adl - _lastema1) * _k1 + _lastema1; - double _ema2 = (_adl - _lastema2) * _k2 + _lastema2; - - _lastlastadl = _lastadl; - _lastadl = _adl; - _lastlastema1 = _lastema1; - _lastema1 = _ema1; - _lastlastema2 = _lastema2; - _lastema2 = _ema2; - - double _adosc = _ema1 - _ema2; - - var ret = (TBar.t, _adosc); - return base.Add(ret, update); - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ALMA_Series.cs b/archive/Calculations/_Updated/ALMA_Series.cs deleted file mode 100644 index 5c62c0dd..00000000 --- a/archive/Calculations/_Updated/ALMA_Series.cs +++ /dev/null @@ -1,129 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -ALMA: Arnaud Legoux Moving Average - The ALMA moving average uses the curve of the Normal (Gauss) distribution, which - can be shifted from 0 to 1. This allows regulating the smoothness and high - sensitivity of the indicator. Sigma is another parameter that is responsible for - the shape of the curve coefficients. This moving average reduces lag of the data - in conjunction with smoothing to reduce noise. - - -Sources: - https://phemex.com/academy/what-is-arnaud-legoux-moving-averages - https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/ - - Discrepancy with Pandas-TA (but passes the validation with Skender.GetAlma) - */ - -public class ALMA_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - private readonly System.Collections.Generic.List _buffer = new(); - private readonly System.Collections.Generic.List _weight; - private double _norm; - private readonly double _offset, _sigma; - - //core constructors - public ALMA_Series(int period, double offset, double sigma, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"ALMA({period})"; - _offset = offset; - _sigma = sigma; - _weight = new(); - } - public ALMA_Series(TSeries source, int period, double offset, double sigma, bool useNaN) : this(period, offset, sigma, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - public ALMA_Series() : this(period: 0, offset: 0.85, sigma: 6.0, useNaN: false) { } - public ALMA_Series(int period) : this(period: period, offset: 0.85, sigma: 6.0, useNaN: false) { } - public ALMA_Series(TBars source) : this(source: source.Close, period: 0, offset: 0.85, sigma: 6.0, useNaN: false) { } - public ALMA_Series(TBars source, int period) : this(source: source.Close, period: period, offset: 0.85, sigma: 6.0, useNaN: false) { } - public ALMA_Series(TBars source, int period, double offset, double sigma, bool useNaN) : this(source.Close, period: period, offset: offset, sigma: sigma, useNaN: false) { } - public ALMA_Series(TSeries source) : this(source, period: 0, offset: 0.85, sigma: 6.0, useNaN: false) { } - public ALMA_Series(TSeries source, int period) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: false) { } - public ALMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: useNaN) { } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, double.NaN), update); - } - - BufferTrim(_buffer, TValue.v, _period, update); - if (_weight.Count < _buffer.Count) - { - for (var i = 0; i < _buffer.Count - _weight.Count; i++) - { - _weight.Add(0.0); - } - } - - - if (_buffer.Count <= _period || _period == 0) - { - var _len = _buffer.Count; - _norm = 0; - var _m = _offset * (_len - 1); - var _s = _len / _sigma; - for (var i = 0; i < _len; i++) - { - var _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s)); - _weight[i] = _wt; - _norm += _wt; - } - } - - double _weightedSum = 0; - for (var i = 0; i < _buffer.Count; i++) - { - _weightedSum += _weight[i] * _buffer[i]; - } - - var _alma = _weightedSum / _norm; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - //reset calculation - public override void Reset() - { - _buffer.Clear(); - _weight.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ATRP_Series.cs b/archive/Calculations/_Updated/ATRP_Series.cs deleted file mode 100644 index 9a4fee50..00000000 --- a/archive/Calculations/_Updated/ATRP_Series.cs +++ /dev/null @@ -1,97 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ATRP: Average True Range Percent - Average True Range Percent is (ATR/Close Price)*100. - This normalizes so it can be compared to other stocks. - -Sources: - https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atrp - - */ - -public class ATRP_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private double _k; - private int _len; - private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; - - //core constructors - public ATRP_Series(int period, bool useNaN) - { - _period = period; - _k = 1.0 / (double)(_period); - _NaN = useNaN; - _len = 0; - Name = $"ATRP({period})"; - } - public ATRP_Series(TBars source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public ATRP_Series() : this(period: 1, useNaN: false) { } - public ATRP_Series(int period) : this(period: period, useNaN: false) { } - public ATRP_Series(TBars source) : this(source, period: 1, useNaN: false) { } - public ATRP_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } - else - { - _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; - _k = (_period == 0) ? 1 / (double)_len : _k; - _len++; - } - - if (_len == 1) { _cm1 = TBar.c; } - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - _cm1 = TBar.c; - - double _atr = 0; - if (this.Count == 0) { _atr = d.v; } - else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } - else { _atr = _k * (d.v - _lastatr) + _lastatr; } - _lastatr = _atr; - double _atrp = 100 * (_atr / TBar.c); - - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atrp); - return base.Add(res, update); - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ATR_Series.cs b/archive/Calculations/_Updated/ATR_Series.cs deleted file mode 100644 index c7e36b0a..00000000 --- a/archive/Calculations/_Updated/ATR_Series.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ATR: wildeR Moving Average - The average true range (ATR) is a price volatility indicator - showing the average price variation of assets within a given time period. - -Sources: - https://en.wikipedia.org/wiki/Average_true_range - https://www.tradingview.com/wiki/Average_True_Range_(ATR) - https://www.investopedia.com/terms/a/atr.asp - - */ - -public class ATR_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private double _k; - private int _len; - private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; - - //core constructors - public ATR_Series(int period, bool useNaN) - { - _period = period; - _k = 1.0 / (double)(_period); - _NaN = useNaN; - _len = 0; - Name = $"ATR({period})"; - } - public ATR_Series(TBars source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public ATR_Series() : this(period: 1, useNaN: false) { } - public ATR_Series(int period) : this(period: period, useNaN: false) { } - public ATR_Series(TBars source) : this(source, period: 1, useNaN: false) { } - public ATR_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } - else - { - _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; - _k = (_period == 0) ? 1 / (double)_len : _k; - _len++; - } - - if (_len == 1) { _cm1 = TBar.c; } - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - _cm1 = TBar.c; - - double _atr = 0; - if (this.Count == 0) { _atr = d.v; } - else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } - else { _atr = _k * (d.v - _lastatr) + _lastatr; } - _lastatr = _atr; - - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atr); - return base.Add(res, update); - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/BBANDS_Series.cs b/archive/Calculations/_Updated/BBANDS_Series.cs deleted file mode 100644 index 1750def4..00000000 --- a/archive/Calculations/_Updated/BBANDS_Series.cs +++ /dev/null @@ -1,121 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -BBANDS: Bollinger Bands® - Price channels created by John Bollinger, depict volatility as standard deviation boundary - line range from a moving average of price. The bands automatically widen when volatility - increases and contract when volatility decreases. Their dynamic nature allows them to be - used on different securities with the standard settings. - - Mid Band = simple moving average (SMA) - Upper Band = SMA + (standard deviation of price x multiplier) - Lower Band = SMA - (standard deviation of price x multiplier) - Bandwidth = Width of the channel: (Upper-Lower)/SMA - %B = The location of the data point within the channel: (Price-Lower)/(Upper/Lower) - Z-Score = number of standard deviations of the data point from SMA - -Sources: - https://www.investopedia.com/terms/b/bollingerbands.asp - https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands - -Note: - Bollinger Bands® is a registered trademark of John A. Bollinger. - - */ - -public class BBANDS_Series : TSeries -{ - protected readonly int _period; - protected readonly double _multiplier; - protected readonly bool _NaN; - protected readonly TSeries _data; - public SMA_Series Mid { get; } - public TSeries Upper { get; } - public TSeries Lower { get; } - public TSeries PercentB { get; } - public TSeries Bandwidth { get; } - public TSeries Zscore { get; } - private readonly SDEV_Series _sdev; - - //core constructors - public BBANDS_Series(int period, double multiplier, bool useNaN) - { - _period = period; - _multiplier = multiplier; - _NaN = useNaN; - Name = $"BBANDS({period})"; - } - public BBANDS_Series(TSeries source, int period, double multiplier, bool useNaN) : this(period, multiplier, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - Upper = new("BB_Up"); - Lower = new("BB_Low"); - Bandwidth = new("BBandwidth"); - PercentB = new("%BBandwidth"); - Zscore = new("Zscore"); - - Mid = new(period, false); - _sdev = new(period, false); - - _data.Pub += Sub; - Add(_data); - } - - public BBANDS_Series() : this(period: 0, multiplier: 2.0, useNaN: false) { } - public BBANDS_Series(int period) : this(period: period, multiplier: 2.0, useNaN: false) { } - public BBANDS_Series(TBars source) : this(source: source.Close, period: 0, multiplier: 2.0, useNaN: false) { } - public BBANDS_Series(TBars source, int period) : this(source: source.Close, period: period, multiplier: 2.0, useNaN: false) { } - public BBANDS_Series(TBars source, int period, double multiplier, bool useNaN) : this(source.Close, period: period, multiplier: multiplier, useNaN: false) { } - public BBANDS_Series(TSeries source) : this(source, period: 0, useNaN: false) { } - public BBANDS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - public BBANDS_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, multiplier: 2.0, useNaN: useNaN) { } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - var _mid = Mid.Add(TValue, update); - var _sd = this._sdev.Add(TValue, update); - var _upper = Upper.Add((TValue.t, _mid.v + _sd.v * _multiplier), update); - var _lower = Lower.Add((TValue.t, _mid.v - _sd.v * _multiplier), update); - double _pbdnd = TValue.v - _lower.v; - double _pbdvr = _upper.v - _lower.v; - PercentB.Add((TValue.t, _pbdnd / _pbdvr), update); - Zscore.Add((TValue.t, (TValue.v - _mid.v) / _sd.v), update); - Bandwidth.Add((TValue.t, _pbdvr / _mid.v), update); - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pbdvr / _mid.v); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - //reset calculation - public override void Reset() - { - Mid.Clear(); - _sdev.Clear(); - Upper.Clear(); - Lower.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/BIAS_Series.cs b/archive/Calculations/_Updated/BIAS_Series.cs deleted file mode 100644 index f8bcb0a3..00000000 --- a/archive/Calculations/_Updated/BIAS_Series.cs +++ /dev/null @@ -1,81 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -BIAS: Rate of change between the source and a moving average. - Bias is a statistical term which means a systematic deviation from the actual value. - -BIAS = (close - SMA) / SMA - = (close / SMA) - 1 - -Sources: - https://en.wikipedia.org/wiki/Bias_of_an_estimator - - */ - -public class BIAS_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly SMA_Series _sma; - - //core constructors - public BIAS_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"BIAS({period})"; - _sma = new(period, false); - } - public BIAS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public BIAS_Series() : this(period: 0, useNaN: false) { } - public BIAS_Series(int period) : this(period: period, useNaN: false) { } - public BIAS_Series(TBars source) : this(source.Close, 0, false) { } - public BIAS_Series(TBars source, int period) : this(source.Close, period, false) { } - public BIAS_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public BIAS_Series(TSeries source) : this(source, 0, false) { } - public BIAS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - var _s = _sma.Add(TValue, update); - double _bias = (TValue.v / ((_s.v != 0) ? _s.v : 1)) - 1; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _bias); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _sma.Reset(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/CCI_Series.cs b/archive/Calculations/_Updated/CCI_Series.cs deleted file mode 100644 index 4ee2d959..00000000 --- a/archive/Calculations/_Updated/CCI_Series.cs +++ /dev/null @@ -1,97 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -CCI: Commodity Channel Index - Commodity Channel Index is a momentum oscillator used to primarily identify overbought - and oversold levels relative to a mean. CCI measures the current price level relative - to an average price level over a given period of time: - - CCI is relatively high when prices are far above their average. - - CCI is relatively low when prices are far below their average. - Using this method, CCI can be used to identify overbought and oversold levels. - -Sources: - https://www.investopedia.com/terms/c/commoditychannelindex.asp - https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci - - */ - -public class CCI_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private readonly System.Collections.Generic.List _tp = new(); - - //core constructors - public CCI_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"CCI({period})"; - } - public CCI_Series(TBars source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public CCI_Series() : this(period: 2, useNaN: false) { } - public CCI_Series(int period) : this(period: period, useNaN: false) { } - public CCI_Series(TBars source) : this(source, period: 2, useNaN: false) { } - public CCI_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - double _tpItem = (TBar.h + TBar.l + TBar.c) / 3.0; - if (update) - { - this._tp[this._tp.Count - 1] = _tpItem; - } - else - { - this._tp.Add(_tpItem); - } - if (this._tp.Count > this._period) { this._tp.RemoveAt(0); } - - // average TP over _tp buffer - double _avgTp = _tp.Average(); - - // average Deviation over _tp buffer - double _avgDv = 0; - for (int i = 0; i < this._tp.Count; i++) { _avgDv += Math.Abs(_avgTp - this._tp[i]); } - _avgDv /= this._tp.Count; - - double _cci = (_avgDv == 0) ? 0 : (this._tp[this._tp.Count - 1] - _avgTp) / (0.015 * _avgDv); - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _cci); - return base.Add(res, update); - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _tp.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/CMO_Series.cs b/archive/Calculations/_Updated/CMO_Series.cs deleted file mode 100644 index 36476603..00000000 --- a/archive/Calculations/_Updated/CMO_Series.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -CMO: Chande Momentum Oscillator - Chande Momentum Oscillator (also known as CMO indicator) was developed by Tushar S. Chande - CMO is similar to other momentum oscillators (e.g. RSI or Stochastics). Alike RSI oscillator, - the CMO values move in the range from -100 to +100 points and its aim is to detect the - overbought and oversold market conditions. CMO calculates the price momentum on both the up - days as well as the down days. The CMO calculation is based on non-smoothed price values - meaning that it can reach its extremes more frequently and the short-time swings are more visible. - -Sources: - https://www.technicalindicators.net/indicators-technical-analysis/144-cmo-chande-momentum-oscillator - - */ - -public class CMO_Series : TSeries -{ - private readonly System.Collections.Generic.List _buff_up = new(); - private readonly System.Collections.Generic.List _buff_dn = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _plast_value, _last_value; - - //core constructors - public CMO_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"CMO({period})"; - } - public CMO_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public CMO_Series() : this(period: 0, useNaN: false) { } - public CMO_Series(int period) : this(period: period, useNaN: false) { } - public CMO_Series(TBars source) : this(source.Close, 0, false) { } - public CMO_Series(TBars source, int period) : this(source.Close, period, false) { } - public CMO_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public CMO_Series(TSeries source) : this(source, 0, false) { } - public CMO_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (update) { _last_value = _plast_value; } else { _plast_value = _last_value; } - BufferTrim(buffer: _buff_up, (TValue.v > _last_value) ? TValue.v - _last_value : 0, period: _period, update: update); - BufferTrim(buffer: _buff_dn, (TValue.v < _last_value) ? _last_value - TValue.v : 0, period: _period, update: update); - _last_value = TValue.v; - double _cmo_up = 0; - double _cmo_dn = 0; - for (int i = 0; i < Math.Min(_buff_up.Count, _buff_dn.Count); i++) - { - _cmo_up += _buff_up[i]; - _cmo_dn += _buff_dn[i]; - } - double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn); - if (_cmo_up + _cmo_dn == 0) { _cmo = 0; } - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _cmo); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buff_up.Clear(); - _buff_dn.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/CUSUM_Series.cs b/archive/Calculations/_Updated/CUSUM_Series.cs deleted file mode 100644 index eef69d44..00000000 --- a/archive/Calculations/_Updated/CUSUM_Series.cs +++ /dev/null @@ -1,80 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -CUSUM: Cumulative Sum (aka Running Total) - SUM across a period provides a rolling sum of all values across the period. - If SUM values would be divided with period, the output would be SMA() - -Sources: - https://en.wikipedia.org/wiki/CUSUM - */ - -public class CUSUM_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public CUSUM_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"CUSUM({period})"; - } - public CUSUM_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public CUSUM_Series() : this(period: 0, useNaN: false) { } - public CUSUM_Series(int period) : this(period: period, useNaN: false) { } - public CUSUM_Series(TBars source) : this(source.Close, 0, false) { } - public CUSUM_Series(TBars source, int period) : this(source.Close, period, false) { } - public CUSUM_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public CUSUM_Series(TSeries source) : this(source, period: 0, useNaN: false) { } - public CUSUM_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sum = 0; - for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; } - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sum); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/DECAY_Series.cs b/archive/Calculations/_Updated/DECAY_Series.cs deleted file mode 100644 index 287b3f45..00000000 --- a/archive/Calculations/_Updated/DECAY_Series.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -DECAY: - Linear decay can be modeled by a straight line with a negative slope of 1/period. - The value decreases in a straight line from the last maximum to 0. - Decay = Last Max - distance/period - - Exponential decay is modeled as an exponential curve with diminishing factor of - 1-1/p - - */ - -public class DECAY_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly bool _exp; - private double _pdecay, _ppdecay; - private readonly double _dfactor; - - //core constructors - public DECAY_Series(int period, bool exponential, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"DECAY({period})"; - _exp = exponential; - _dfactor = (_exp) ? 1.0 - 1.0 / (double)_period : 1 / (double)_period; - _pdecay = _ppdecay = 0; - } - public DECAY_Series(TSeries source, int period, bool exponential, bool useNaN) : this(period, exponential, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public DECAY_Series() : this(period: 0, exponential: false, useNaN: false) { } - public DECAY_Series(int period) : this(period: period, exponential: false, useNaN: false) { } - public DECAY_Series(TBars source) : this(source.Close, period: 0, exponential: false, useNaN: false) { } - public DECAY_Series(TBars source, int period) : this(source.Close, period: period, exponential: false, useNaN: false) { } - public DECAY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, exponential: false, useNaN) { } - public DECAY_Series(TSeries source) : this(source, period: 0, exponential: false, useNaN: false) { } - public DECAY_Series(TSeries source, int period) : this(source: source, period: period, exponential: false, useNaN: false) { } - public DECAY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, exponential: false, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - if (update) { _pdecay = _ppdecay; } - else { _ppdecay = _pdecay; } - - if (this.Count == 0) { _pdecay = TValue.v; } - double _decay = Math.Max(TValue.v, Math.Max((_exp) ? _pdecay * _dfactor : _pdecay - _dfactor, 0)); - _pdecay = _decay; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _decay); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _pdecay = _ppdecay = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/DEMA_Series.cs b/archive/Calculations/_Updated/DEMA_Series.cs deleted file mode 100644 index 1288d666..00000000 --- a/archive/Calculations/_Updated/DEMA_Series.cs +++ /dev/null @@ -1,144 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -DEMA: Double Exponential Moving Average - DEMA uses EMA(EMA()) to calculate smoother Exponential moving average. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/ - -Remark: - ema1 = EMA(close, length) - ema2 = EMA(ema1, length) - DEMA = 2 * ema1 - ema2 - - */ - -public class DEMA_Series : TSeries -{ - private double _k; - private double _sum, _oldsum; - private double _lastema1, _oldema1, _lastema2, _oldema2; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructor - public DEMA_Series(int period, bool useNaN, bool useSMA) - { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"DEMA({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _sum = _oldsum = _lastema1 = _lastema2 = 0; - } - //generic constructors (source) - - public DEMA_Series() : this(0, false, true) { } - public DEMA_Series(int period) : this(period, false, true) { } - public DEMA_Series(TBars source) : this(source.Close, 0, false) { } - public DEMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public DEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public DEMA_Series(TSeries source, int period) : this(source, period, false, true) { } - public DEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - public DEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (update) - { - _lastema1 = _oldema1; - _lastema2 = _oldema2; - _sum = _oldsum; - } - else - { - _oldema1 = _lastema1; - _oldema2 = _lastema2; - _oldsum = _sum; - _len++; - } - - if (_period == 0) - { - _k = 2.0 / (_len + 1); - } - - double _ema1, _ema2, _dema; - if (Count == 0) - { - _ema1 = _ema2 = _sum = TValue.v; - } - else if (_len <= _period && _useSMA && _period != 0) - { - _sum += TValue.v; - _ema1 = _sum / Math.Min(_len, _period); - _ema2 = _ema1; - } - else - { - _ema1 = (TValue.v - _lastema1) * _k + _lastema1; - _ema2 = (_ema1 - _lastema2) * _k + _lastema2; - } - - _dema = 2 * _ema1 - _ema2; - - _lastema1 = double.IsNaN(_ema1) ? _lastema1 : _ema1; - _lastema2 = double.IsNaN(_ema2) ? _lastema2 : _ema2; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dema); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) - { - return (DateTime.Today, double.NaN); - } - - foreach (var item in data) - { - Add(item, false); - } - - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) - { - return Add(_data.Last, update); - } - - public (DateTime t, double v) Add() - { - return Add(_data.Last, false); - } - - private new void Sub(object source, TSeriesEventArgs e) - { - Add(_data.Last, e.update); - } - - //reset calculation - public override void Reset() - { - _sum = _oldsum = _lastema1 = _lastema2 = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/DWMA_Series.cs b/archive/Calculations/_Updated/DWMA_Series.cs deleted file mode 100644 index 7c03b8a9..00000000 --- a/archive/Calculations/_Updated/DWMA_Series.cs +++ /dev/null @@ -1,143 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -/* -DWMA: Double Weighted Moving Average - The weights are decreasing over the period with p^2 decay - and the most recent data has the heaviest weight. - - */ - -public class DWMA_Series : TSeries -{ - private readonly List _buffer = new(); - private List _weights; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected int _len; - - //core constructors - public DWMA_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"DWMA({period})"; - _len = 0; - _weights = CalculateWeights(_period); - } - - public DWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - public DWMA_Series() : this(0, false) - { - } - - public DWMA_Series(int period) : this(period, false) - { - } - - public DWMA_Series(TBars source) : this(source.Close, 0, false) - { - } - - public DWMA_Series(TBars source, int period) : this(source.Close, period, false) - { - } - - public DWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) - { - } - - public DWMA_Series(TSeries source, int period) : this(source, period, false) - { - } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(_buffer, TValue.v, _period, update); - if (_period == 0) - { - _len++; - _weights = CalculateWeights(_len); - } - - double _dwma = 0, _wsum = 0; - var bufferCount = _buffer.Count; - - var lockObj = new object(); - Parallel.For(0, bufferCount, i => - { - var temp = _buffer[i] * _weights[i]; - lock (lockObj) - { - _dwma += temp; - _wsum += _weights[i]; - } - }); - _dwma /= _wsum; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dwma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) - { - return (DateTime.Today, double.NaN); - } - - foreach (var item in data) - { - Add(item, false); - } - - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) - { - return Add(_data.Last, update); - } - - public (DateTime t, double v) Add() - { - return Add(_data.Last, false); - } - - private new void Sub(object source, TSeriesEventArgs e) - { - Add(_data.Last, e.update); - } - - //calculating weights - private static List CalculateWeights(int period) - { - var weights = new List(period); - for (var i = 0; i < period; i++) - { - weights.Add((i + 1) * (i + 1)); - } - - return weights; - } - - //reset calculation - public override void Reset() - { - _len = 0; - _buffer.Clear(); - _weights = CalculateWeights(_period); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/EMA_Series.cs b/archive/Calculations/_Updated/EMA_Series.cs deleted file mode 100644 index 7f49566d..00000000 --- a/archive/Calculations/_Updated/EMA_Series.cs +++ /dev/null @@ -1,136 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -EMA: Exponential Moving Average - EMA needs very short history buffer and calculates the EMA value using just the - previous EMA value. The weight of the new datapoint (k) is k = 2 / (period-1) - -Sources: - https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages - https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp - https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA - -Issues: - There is no consensus what the first EMA value should be - a zero, a first - datapoint, or an average of the initial Period bars. All three starting methods - converge within 20+ bars to the same moving average. Most implementations (including this one) - use SMA() for the first Period bars as a seeding value for EMA. - - */ - -public class EMA_Series : TSeries -{ - private double _k; - private double _lastema, _oldema; - private double _sum, _oldsum; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - - public EMA_Series(int period, bool useNaN, bool useSMA) - { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"EMA({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _sum = _oldsum = _lastema = _oldema = 0; - } - public EMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public EMA_Series() : this(0, false, true) { } - public EMA_Series(int period) : this(period, false, true) { } - public EMA_Series(TBars source) : this(source.Close, 0, false) { } - public EMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public EMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public EMA_Series(TSeries source, int period) : this(source, period, false, true) { } - public EMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (update) - { - _lastema = _oldema; - _sum = _oldsum; - } - else - { - _oldema = _lastema; - _oldsum = _sum; - _len++; - } - - double _ema = 0; - if (_period == 0) - { - _k = 2.0 / (_len + 1); - } - - if (Count == 0) - { - _ema = _sum = TValue.v; - } - else if (_len <= _period && _useSMA && _period != 0) - { - _sum += TValue.v; - if (_period != 0 && _len > _period) - { - _sum -= _data[Count - _period - (update ? 1 : 0)].v; - } - - _ema = _sum / Math.Min(_len, _period); - } - else - { - _ema = _k * (TValue.v - _lastema) + _lastema; - } - - _lastema = double.IsNaN(_ema) ? _lastema : _ema; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ema); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _sum = _oldsum = _lastema = _oldema = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ENTROPY_Series.cs b/archive/Calculations/_Updated/ENTROPY_Series.cs deleted file mode 100644 index 14da1862..00000000 --- a/archive/Calculations/_Updated/ENTROPY_Series.cs +++ /dev/null @@ -1,97 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -ENTROPY: - Introduced by Claude Shannon in 1948, entropy measures the unpredictability - of the data, or equivalently, of its average information. - -Calculation: - P = close / Σ(close) - ENTROPY = Σ(-P * Log(P) / Log(base)) - -Sources: - https://en.wikipedia.org/wiki/Entropy_(information_theory) - https://math.stackexchange.com/questions/3428693/how-to-calculate-entropy-from-a-set-of-correlated-samples - - */ - -public class ENTROPY_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly double _logbase; - private readonly System.Collections.Generic.List _buffer = new(); - private readonly System.Collections.Generic.List _buff2 = new(); - - //core constructors - public ENTROPY_Series(int period, double logbase, bool useNaN) - { - _period = period; - _NaN = useNaN; - _logbase = logbase; - Name = $"ENTROPY({period})"; - } - public ENTROPY_Series(TSeries source, int period, double logbase, bool useNaN) : this(period, logbase, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public ENTROPY_Series() : this(period: 0, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(int period) : this(period: period, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TBars source) : this(source.Close, period: 0, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TBars source, int period) : this(source.Close, period, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, logbase: 2.0, useNaN: useNaN) { } - public ENTROPY_Series(TSeries source) : this(source, period: 0, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TSeries source, int period) : this(source: source, period: period, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, logbase: 2.0, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - double _sum = _buffer.Sum(); - double _pp = this._buffer[^1] / _sum; - double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase); - BufferTrim(_buff2, _ppp, _period, update); - double _entp = _buff2.Sum(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _entp); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - _buff2.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/FWMA_Series.cs b/archive/Calculations/_Updated/FWMA_Series.cs deleted file mode 100644 index eceecc6b..00000000 --- a/archive/Calculations/_Updated/FWMA_Series.cs +++ /dev/null @@ -1,105 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Numerics; -using System.Linq; - -/* -FWMA: Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average - (WMA) where the weights are based on the Fibonacci Sequence. - - */ -public class FWMA_Series : TSeries -{ - private readonly List _buffer = new(); - private List _weights; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected int _len; - - public FWMA_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"FWMA({period})"; - _len = 0; - _weights = CalculateWeights(_period); - } - - public FWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - public FWMA_Series() : this(period: 0, useNaN: false) { } - public FWMA_Series(int period) : this(period: period, useNaN: false) { } - public FWMA_Series(TBars source) : this(source.Close, 0, false) { } - public FWMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public FWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public FWMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - if (_period == 0) - { - _len++; - _weights = CalculateWeights(_len); - } - double _fwma = 0; - double totalWeights = _weights.Sum(); - object lockObj = new object(); - Parallel.For(0, _buffer.Count, i => - { - double temp = _buffer[i] * _weights[i]; - lock (lockObj) { _fwma += temp; } - }); - _fwma /= totalWeights; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fwma); - return base.Add(res, update); - } - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - private static List CalculateWeights(int period) - { - //to prevent overflow, max period can be no more than 1476 - period = (period > 1476) ? 1476 : period; - List weights = new List(period); - BigInteger a = 0; - BigInteger b = 1; - for (int i = 0; i < period; i++) - { - BigInteger temp = a; - a = b; - b = temp + b; - weights.Add((double)Decimal.Parse(a.ToString())); - } - return weights; - } - - public override void Reset() - { - _weights = CalculateWeights(_period); - _buffer.Clear(); - } -} diff --git a/archive/Calculations/_Updated/HEMA_Series.cs b/archive/Calculations/_Updated/HEMA_Series.cs deleted file mode 100644 index 0d45962f..00000000 --- a/archive/Calculations/_Updated/HEMA_Series.cs +++ /dev/null @@ -1,133 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -HEMA: Hull-EMA Moving Average - a hybrid indicator - Modified HUll Moving Average; instead of using WMA (Weighted MA) for calculation, - HEMA uses EMA for Hull's formula: - -EMA1 = EMA(n/2) of price - where k = 4/(n/2 +1) -EMA2 = EMA(n) of price - where k = 3/(n+1) -Raw HMA = (2 * EMA1) - EMA2 -EMA3 = EMA(sqrt(n)) of Raw HMA - where k = 2/(sqrt(n)+1) - */ - -public class HEMA_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _k1, _k2, _k3; - private int _len; - private double _lastema1, _oldema1; - private double _lastema2, _oldema2; - private double _lasthema, _oldhema; - - //core constructors - public HEMA_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"HEMA({period})"; - (_k1, _k2, _k3) = CalculateK(_period); - _len = 0; - _lastema1 = _oldema1 = _lastema2 = _oldema2 = _lasthema = _oldhema = 0; - } - public HEMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public HEMA_Series() : this(period: 0, useNaN: false) { } - public HEMA_Series(int period) : this(period: period, useNaN: false) { } - public HEMA_Series(TBars source) : this(source.Close, 0, false) { } - public HEMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public HEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public HEMA_Series(TSeries source) : this(source, 0, false) { } - public HEMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (update) - { - _lastema1 = _oldema1; - _lastema2 = _oldema2; - _lasthema = _oldhema; - } - else - { - _oldema1 = _lastema1; - _oldema2 = _lastema2; - _oldhema = _lasthema; - } - double _ema1, _ema2, _hema; - if (_period == 0) - { - _len++; - (_k1, _k2, _k3) = CalculateK(_len); - } - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, double.NaN), update); - } - else if (this.Count == 0) - { - _ema1 = _ema2 = _hema = TValue.v; - } - else - { - _ema1 = _k1 * (TValue.v - _lastema1) + _lastema1; - _ema2 = _k2 * (TValue.v - _lastema2) + _lastema2; - _hema = _k3 * (((2 * _ema1) - _ema2) - _lasthema) + _lasthema; - } - - _lastema1 = _ema1; - _lastema2 = _ema2; - _lasthema = _hema; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hema); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _lastema1 = _lastema2 = _lasthema = 0; - _oldema1 = _oldema2 = _oldhema = 0; - _len = 0; - } - - public static (double k1, double k2, double k3) CalculateK(int len) - { - double k1 = 8 / (double)(len + 7); - double k2 = 3 / (double)(len + 2); - double k3 = 2 / Math.Sqrt(len + 3); - - return (k1, k2, k3); - } - -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/HMA_Series.cs b/archive/Calculations/_Updated/HMA_Series.cs deleted file mode 100644 index 30d9087a..00000000 --- a/archive/Calculations/_Updated/HMA_Series.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -HMA: Hull Moving Average - Developed by Alan Hull, an extremely fast and smooth moving average; almost - eliminates lag altogether and manages to improve smoothing at the same time. - -Sources: - https://alanhull.com/hull-moving-average - https://school.stockcharts.com/doku.php?id=technical_indicators:hull_moving_average - -WMA1 = WMA(n/2) of price -WMA2 = WMA(n) of price -Raw HMA = (2 * WMA1) - WMA2 -HMA = WMA(sqrt(n)) of Raw HMA - - */ - -public class HMA_Series : TSeries -{ - protected int _period, _period2, _psqrt; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected WMA_Series _wma1, _wma2, _wma3; - - //core constructors - public HMA_Series(int period, bool useNaN) - { - _period = period; - _period2 = period / 2; - _psqrt = (int)Math.Sqrt(period); - _NaN = useNaN; - _wma1 = new(Math.Max(_period2, 1), false); - _wma2 = new(Math.Max(_period, 1), false); - _wma3 = new(Math.Max(_psqrt, 1), useNaN); - Name = $"HMA({period})"; - } - public HMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public HMA_Series() : this(period: 0, useNaN: false) { } - public HMA_Series(int period) : this(period: period, useNaN: false) { } - public HMA_Series(TBars source) : this(source.Close, 0, false) { } - public HMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public HMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public HMA_Series(TSeries source) : this(source, 0, false) { } - public HMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (_period == 0) - { - _wma1.Len = this.Count / 2; - _wma2.Len = this.Count; - _wma1.Len = (int)Math.Sqrt(this.Count); - } - double _w1 = _wma1.Add(TValue, update).v; - double _w2 = _wma2.Add(TValue, update).v; - double _hma = _wma3.Add((2 * _w1) - _w2, update).v; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _wma1.Reset(); - _wma2.Reset(); - _wma3.Reset(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/HWMA_Series.cs b/archive/Calculations/_Updated/HWMA_Series.cs deleted file mode 100644 index b58ea0b2..00000000 --- a/archive/Calculations/_Updated/HWMA_Series.cs +++ /dev/null @@ -1,146 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -HWMA: Holt-Winter Moving Average - Indicator HWMA (Holt-Winter Moving Average) is a three-parameter moving - average by the Holt-Winter method; Holt-Winters Exponential Smoothing is - used for forecasting time series data that exhibits both a trend and a - seasonal variation. - - -Sources: - https://timeseriesreasoning.com/contents/holt-winters-exponential-smoothing/ - https://www.mql5.com/en/code/20856 - -nA - smoothed series (from 0 to 1) -nB - assess the trend (from 0 to 1) -nC - assess seasonality (from 0 to 1) - -Heuristic for determining alpha, beta, and gamma from period: - alpha = 2 / (1 + period) - beta = 1 / period - gamma = 1 / period - -F[i] = (1-nA) * (F[i-1] + V[i-1] + 0.5 * A[i-1]) + nA * Price[i] -V[i] = (1-nB) * (V[i-1] + A[i-1]) + nB * (F[i] - F[i-1]) -A[i] = (1-nC) * A[i-1] + nC * (V[i] - V[i-1]) -HWMA[i] = F[i] + V[i] + 0.5 * A[i] - - */ - -public class HWMA_Series : TSeries -{ - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - double _nA, _nB, _nC; - double _pF, _pV, _pA; - double _ppF, _ppV, _ppA; - - //core constructors - - public HWMA_Series(double nA, double nB, double nC, bool useNaN) - { - _period = (int)((2 - nA) / nA); - _nA = nA; - _nB = nB; - _nC = nC; - _NaN = useNaN; - Name = $"HWMA({_period})"; - _len = 0; - } - public HWMA_Series(TSeries source, double nA, double nB, double nC, bool useNaN = false) : this(nA, nB, nC, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public HWMA_Series() : this(period: 0, useNaN: false) { } - public HWMA_Series(int period) : this(period, useNaN: false) { } - public HWMA_Series(int period, bool useNaN) : this(nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN) - { - _period = period; - } - public HWMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } - public HWMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public HWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public HWMA_Series(TSeries source, int period) : this(source, period, false) { } - public HWMA_Series(TSeries source, int period, bool useNaN) : this(source, nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - double _F, _V, _A; - if (_len == 0) { _pF = TValue.v; _pA = _pV = 0; } - - if (update) { _pF = _ppF; _pV = _ppV; _pA = _ppA; } - else - { - _ppF = _pF; - _ppV = _pV; - _ppA = _pA; - _len++; - } - - if (_period == 0) - { - _nA = 2 / (1 + (double)_len); - _nB = 1 / (double)_len; - _nC = 1 / (double)_len; - } - if (_period == 1) - { - _nA = 1; - _nB = 0; - _nC = 0; - } - - _F = (1 - _nA) * (_pF + _pV + 0.5 * _pA) + _nA * TValue.v; - _V = (1 - _nB) * (_pV + _pA) + _nB * (_F - _pF); - _A = (1 - _nC) * _pA + _nC * (_V - _pV); - - double _hwma = _F + _V + 0.5 * _A; - _pF = _F; - _pV = _V; - _pA = _A; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hwma); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/JMA_Series.cs b/archive/Calculations/_Updated/JMA_Series.cs deleted file mode 100644 index 8d5b9af0..00000000 --- a/archive/Calculations/_Updated/JMA_Series.cs +++ /dev/null @@ -1,176 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -JMA: Jurik Moving Average - Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the - underlying activity. It has extremely low lag, is very smooth and is responsive - to market gaps. - -Sources: - https://c.mql5.com/forextsd/forum/164/jurik_1.pdf - https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/ - -Issues: - Real JMA algorithm is not published and this formula is derived through - deduction and reverse analysis of JMA behavior. It is really close, but not - exact - published JMA tests against JMA.CSV fail with small deviation. The - original algo is slightly different, yet this approximation is close enough. - - */ - -public class JMA_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly System.Collections.Generic.List volty_short = new(); - private readonly System.Collections.Generic.List vsum_buff = new(); - private readonly double pr; - private double upperBand, lowerBand, vsum, Kv; - private double prev_ma1, prev_det0, prev_det1, prev_vsum, prev_jma; - private double p_upperBand, p_lowerBand, p_Kv, p_prev_ma1, p_prev_det0, p_prev_det1, p_prev_vsum, p_prev_jma; - private readonly int _voltyS, _voltyL; - - //core constructors - public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"JMA({period})"; - upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0; - pr = (phase * 0.01) + 1.5; - if (phase < -100) { pr = 0.5; } - if (phase > 100) { pr = 2.5; } - _voltyS = vshort; - _voltyL = vlong; - } - - public JMA_Series(TSeries source, int period, double phase, int vshort, int vlong, bool useNaN) : this(period, phase, vshort, vlong, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public JMA_Series() : this(period: 0, phase: 0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(int period) : this(period: period, phase: 0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TBars source) : this(source.Close, period: 0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TBars source, int period) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { } - public JMA_Series(TSeries source) : this(source, period: 0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TSeries source, int period) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; } - if (update) - { - upperBand = p_upperBand; - lowerBand = p_lowerBand; - Kv = p_Kv; - prev_vsum = p_prev_vsum; - prev_ma1 = p_prev_ma1; - prev_det0 = p_prev_det0; - prev_det1 = p_prev_det1; - prev_jma = p_prev_jma; - } - else - { - p_upperBand = upperBand; - p_lowerBand = lowerBand; - p_Kv = Kv; - p_prev_vsum = prev_vsum; - p_prev_ma1 = prev_ma1; - p_prev_det0 = prev_det0; - p_prev_det1 = prev_det1; - p_prev_jma = prev_jma; - } - - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, double.NaN), update); - } - - // from Tvalue to volty - double del1 = TValue.v - upperBand; - double del2 = TValue.v - lowerBand; - upperBand = (del1 > 0) ? TValue.v : TValue.v - (Kv * del1); - lowerBand = (del2 < 0) ? TValue.v : TValue.v - (Kv * del2); - double volty = Math.Abs(del1) > Math.Abs(del2) ? Math.Abs(del1) : - (Math.Abs(del1) < Math.Abs(del2) ? Math.Abs(del2) : - Math.Abs(0.5 * (del1 + del2))); - - //// from volty to avolty - if (update) { volty_short[volty_short.Count - 1] = volty; } - else { volty_short.Add(volty); } - if (volty_short.Count > _voltyS) { volty_short.RemoveAt(0); } - vsum = prev_vsum + 0.1 * (volty - volty_short.First()); - prev_vsum = vsum; - if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; } - else { vsum_buff.Add(vsum); } - if (vsum_buff.Count > _voltyL) { vsum_buff.RemoveAt(0); } - double avolty = 0; - for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; } - avolty /= vsum_buff.Count; - - /// from avolty to rolty - double rvolty = (avolty != 0) ? volty / avolty : 0; - double len1 = (Math.Log(Math.Sqrt(_period)) / Math.Log(2.0)) + 2; - if (len1 < 0) { len1 = 0; } - - double pow1 = Math.Max(len1 - 2.0, 0.5); - if (rvolty > Math.Pow(len1, 1.0 / pow1)) { rvolty = Math.Pow(len1, 1.0 / pow1); } - if (rvolty < 1) { rvolty = 1; } - - //// from rvolty to second smoothing - double pow2 = Math.Pow(rvolty, pow1); - double beta = 0.45 * (_period - 1) / (0.45 * (_period - 1) + 2); - Kv = Math.Pow(beta, Math.Sqrt(pow2)); - double alpha = Math.Pow(beta, pow2); - double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1; - prev_ma1 = ma1; - - double det0 = (1 - beta) * (TValue.v - ma1) + beta * prev_det0; - prev_det0 = det0; - double ma2 = ma1 + pr * det0; - - double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - prev_jma)) + (alpha * alpha * prev_det1); - prev_det1 = det1; - double jma = prev_jma + det1; - prev_jma = jma; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : jma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/KAMA_Series.cs b/archive/Calculations/_Updated/KAMA_Series.cs deleted file mode 100644 index f839d4aa..00000000 --- a/archive/Calculations/_Updated/KAMA_Series.cs +++ /dev/null @@ -1,118 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -KAMA: Kaufman's Adaptive Moving Average - Created in 1988 by American quantitative finance theorist Perry J. Kaufman and is known as - Kaufman's Adaptive Moving Average (KAMA). Even though the method was developed as early as 1972, - it was not until the popular book titled "Trading Systems and Methods" that it was made widely - available to the public. Unlike other conventional moving averages systems, the Kaufman's Adaptive - Moving Average, considers market volatility apart from price fluctuations. - - KAMA[i] = KAMA[i-1] + SC * ( price - KAMA[i-1] ) - -Sources: - https://www.tutorialspoint.com/kaufman-s-adaptive-moving-average-kama-formula-and-how-does-it-work - https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/ - https://www.technicalindicators.net/indicators-technical-analysis/152-kama-kaufman-adaptive-moving-average - -Remark: - If useNaN:true argument is provided, KAMA starts calculating values from [period] bar onwards. - Without useNaN argument (default setting), KAMA starts calculating values from bar 1 - and yields - slightly different results for the first 50 bars - and then converges with the other one. - - */ - -public class KAMA_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - private double _lastkama, _lastlastkama; - private readonly double _scFast, _scSlow; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public KAMA_Series(int period, int fast, int slow, bool useNaN) - { - _period = period; - _NaN = useNaN; - _scFast = 2.0 / (((period < fast) ? period : fast) + 1); - _scSlow = 2.0 / (slow + 1); - _lastkama = _lastlastkama = 0; - Name = $"KAMA({period})"; - } - public KAMA_Series(TSeries source, int period, int fast, int slow, bool useNaN) : this(period, fast, slow, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public KAMA_Series() : this(period: 0, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(int period) : this(period: period, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TBars source) : this(source.Close, period: 0, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TBars source, int period) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: useNaN) { } - public KAMA_Series(TSeries source) : this(source, period: 0, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TSeries source, int period) : this(source: source, period: period, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, fast: 2, slow: 30, useNaN: useNaN) { } - public KAMA_Series(TSeries source, int period, int fast, int slow) : this(source: source, period: period, fast: fast, slow: slow, useNaN: false) { } - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - - if (update) { _lastkama = _lastlastkama; } - else { _lastlastkama = _lastkama; } - BufferTrim(buffer: _buffer, value: TValue.v, period: _period + 1, update: update); - - double _kama = 0; - if (this.Count < _period) { _kama = TValue.v; } - else - { - double _change = Math.Abs(_buffer[^1] - _buffer[(_buffer.Count > _period + 1) ? 1 : 0]); - double _sumpv = 0; - for (int i = 1; i < _buffer.Count; i++) { _sumpv += Math.Abs(_buffer[(_buffer.Count > 0) ? i : 0] - _buffer[i - 1]); } - double _er = (_sumpv == 0) ? 0 : _change / _sumpv; - double _sc = (_er * (_scFast - _scSlow)) + _scSlow; - _kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama))); - } - _lastkama = _kama; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kama); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - _lastkama = _lastlastkama = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/KURTOSIS_Series.cs b/archive/Calculations/_Updated/KURTOSIS_Series.cs deleted file mode 100644 index f632407a..00000000 --- a/archive/Calculations/_Updated/KURTOSIS_Series.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -KURTOSIS: Kurtosis of population - Kurtosis characterizes the relative peakedness or flatness of a distribution - compared with the normal distribution. Positive kurtosis indicates a relatively - peaked distribution. Negative kurtosis indicates a relatively flat distribution. - - The normal curve is called Mesokurtic curve. If the curve of a distribution is - more outlier prone (or heavier-tailed) than a normal or mesokurtic curve then - it is referred to as a Leptokurtic curve. If a curve is less outlier prone (or - lighter-tailed) than a normal curve, it is called as a platykurtic curve. - -Calculation: - sum4 = Σ(close-SMA)^4 - sum2 = (Σ(close-SMA)^2)^2 - KURTOSIS = length * (sum4/sum2) - -Sources: - https://en.wikipedia.org/wiki/Kurtosis - https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ - - */ - -public class KURTOSIS_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly System.Collections.Generic.List _buffer = new(); - - //core constructors - public KURTOSIS_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"KURTOSIS({period})"; - } - public KURTOSIS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public KURTOSIS_Series() : this(period: 0, useNaN: false) { } - public KURTOSIS_Series(int period) : this(period: period, useNaN: false) { } - public KURTOSIS_Series(TSeries source) : this(source, period: 0, useNaN: false) { } - public KURTOSIS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - double _n = _buffer.Count; - double _avg = _buffer.Average(); - - double _s2 = 0; - double _s4 = 0; - for (int i = 0; i < this._buffer.Count; i++) - { - _s2 += (_buffer[i] - _avg) * (_buffer[i] - _avg); - _s4 += (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg); - } - - double _Vx = _s2 / (_n - 1); - double _kurt = (_n > 3) ? - (_n * (_n + 1) * _s4) / (_Vx * _Vx * (_n - 3) * (_n - 1) * (_n - 2)) - (3 * (_n - 1) * (_n - 1) / ((_n - 2) * (_n - 3))) //using Sheskin Algo - : (_s2 * _s2) / _n - 3; //using Snedecor and Cochran (1967) algo - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kurt); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MACD_Series.cs b/archive/Calculations/_Updated/MACD_Series.cs deleted file mode 100644 index c23e1eca..00000000 --- a/archive/Calculations/_Updated/MACD_Series.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MACD: Moving Average Convergence/Divergence - Moving average convergence divergence (MACD) is a trend-following momentum - indicator that shows the relationship between two moving averages of a series. - The MACD is calculated by subtracting the 26-period exponential moving average (EMA) - from the 12-period EMA. MACD Signal is 9-day EMA of MACD. - - */ - -public class MACD_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - - protected readonly int _slow, _fast, _signal; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly EMA_Series _TSlow; - private readonly EMA_Series _TFast; - public EMA_Series Signal { get; } - - //core constructors - public MACD_Series(int slow = 26, int fast = 12, int signal = 9, bool useNaN = false) - { - _slow = slow; - _fast = fast; - _signal = signal; - _NaN = useNaN; - Name = $"MACD({slow},{fast},{signal})"; - _TSlow = new(slow, useNaN: false, useSMA: true); - _TFast = new(fast, useNaN: false, useSMA: true); - Signal = new(signal, useNaN: false, useSMA: true); - } - public MACD_Series(TSeries source, int slow, int fast, int signal, bool useNaN) : this(slow, fast, signal, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MACD_Series(TSeries source) : this(source: source, slow: 26, fast: 12, signal: 9, useNaN: false) { } - public MACD_Series(TSeries source, int slow, int fast, int signal) : this(source: source, slow: slow, fast: fast, signal: signal, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - - var _sslow = _TSlow.Add(TValue, update); - var _sfast = _TFast.Add(TValue, update); - Signal.Add((TValue.t, _sfast.v - _sslow.v)); - - var res = (TValue.t, Count < _fast - 1 && _NaN ? double.NaN : _sfast.v - _sslow.v); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAD_Series.cs b/archive/Calculations/_Updated/MAD_Series.cs deleted file mode 100644 index 23a7bf05..00000000 --- a/archive/Calculations/_Updated/MAD_Series.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAD: Mean Absolute Deviation - Also known as AAD - Average Absolute Deviation, to differentiate it from Median Absolute Deviation - MAD defines the degree of variation across the series. - -Calculation: - MAD = Σ(|close-SMA|) / period - -Sources: - https://en.wikipedia.org/wiki/Average_absolute_deviation - - */ - -public class MAD_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAD_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MAD({period})"; - } - public MAD_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAD_Series() : this(period: 0, useNaN: false) { } - public MAD_Series(int period) : this(period: period, useNaN: false) { } - public MAD_Series(TBars source) : this(source.Close, 0, false) { } - public MAD_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAD_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAD_Series(TSeries source) : this(source, 0, false) { } - public MAD_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - double _mad = 0; - for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); } - _mad /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mad); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAE_Series.cs b/archive/Calculations/_Updated/MAE_Series.cs deleted file mode 100644 index 566b47c2..00000000 --- a/archive/Calculations/_Updated/MAE_Series.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAE: Mean Absolute Error - Defined as a Mean (Average) of the absolute difference between actual and estimated values. - MAE = (1/n) * Σ|y_i - MA_i| - -Sources: - https://en.wikipedia.org/wiki/Mean_absolute_error - - */ - -public class MAE_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MSE({period})"; - } - public MAE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAE_Series() : this(period: 0, useNaN: false) { } - public MAE_Series(int period) : this(period: period, useNaN: false) { } - public MAE_Series(TBars source) : this(source.Close, 0, false) { } - public MAE_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAE_Series(TSeries source) : this(source, 0, false) { } - public MAE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _mae = 0; - for (int i = 0; i < _buffer.Count; i++) { _mae += Math.Abs(_buffer[i] - _sma); } - _mae /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mae); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAMA_Series.cs b/archive/Calculations/_Updated/MAMA_Series.cs deleted file mode 100644 index 538f66a2..00000000 --- a/archive/Calculations/_Updated/MAMA_Series.cs +++ /dev/null @@ -1,207 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -MAMA: MESA Adaptive Moving Average - Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of - high/low price that uses classic electrical radio-frequency signal processing algorithms - to reduce noise. - - KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 ) - -Sources: - https://mesasoftware.com/papers/MAMA.pdf - https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/ - - */ - -public class MAMA_Series : TSeries -{ - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - private double sumPr; - private double fastl, slowl; - private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt; - private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama; - public TSeries Fama { get; } - private double mamaseed, famaseed; - - //core constructors - - public MAMA_Series(double fastlimit, double slowlimit, bool useNaN) - { - _period = (int)(2 / fastlimit) - 1; - fastl = fastlimit; - slowl = slowlimit; - Fama = new TSeries(); - _NaN = useNaN; - Name = $"MAMA({_period})"; - _len = 0; - } - public MAMA_Series(TSeries source, double fastlimit, double slowlimit, bool useNaN = false) : this(fastlimit, slowlimit, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAMA_Series() : this(period: 0, useNaN: false) { } - public MAMA_Series(int period) : this(period, useNaN: false) { } - public MAMA_Series(int period, bool useNaN) : this(fastlimit: 2 / (period + 1), slowlimit: 0.2 / (period + 1), useNaN) - { - _period = period; - } - public MAMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } - public MAMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAMA_Series(TSeries source, int period) : this(source, period, false) { } - public MAMA_Series(TSeries source, int period, bool useNaN) : this(source, fastlimit: 2 / ((double)period + 1), slowlimit: 0.2 / ((double)period + 1), useNaN: useNaN) { } - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - if (!update) - { - // roll forward (oldx = x) - pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i; - i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i; - q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i; - dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i; - sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i; - i2.io = i2.i1; i2.i1 = i2.i; q2.io = q2.i1; q2.i1 = q2.i; - re.io = re.i1; re.i1 = re.i; im.io = im.i1; im.i1 = im.i; - pd.io = pd.i1; pd.i1 = pd.i; ph.io = ph.i1; ph.i1 = ph.i; - mama.io = mama.i1; mama.i1 = mama.i; - fama.io = fama.i1; - fama.i1 = fama.i; - _len++; - } - if (_period == 0) - { - fastl = 2 / (double)_len; - slowl = fastl * 0.1; - } - if (_period == 1) - { - fastl = 1; - slowl = 1; - } - var i = _len - 1; - pr.i = TValue.v; - if (i > 5) - { - var adj = 0.075 * pd.i1 + 0.54; - - // smooth and detrender - sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10; - dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj; - - // in-phase and quadrature - q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj; - i1.i = dt.i3; - - // advance the phases by 90 degrees - double jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj; - double jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj; - - // phasor addition for 3-bar averaging - i2.i = i1.i - jQ; - q2.i = q1.i + jI; - - i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it - q2.i = 0.2 * q2.i + 0.8 * q2.i1; - - // homodyne discriminator - re.i = i2.i * i2.i1 + q2.i * q2.i1; - im.i = i2.i * q2.i1 - q2.i * i2.i1; - - re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it - im.i = 0.2 * im.i + 0.8 * im.i1; - - // calculate period - pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d; - - // adjust period to thresholds - pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i; - pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i; - pd.i = pd.i < 6d ? 6d : pd.i; - pd.i = pd.i > 50d ? 50d : pd.i; - - // smooth the period - pd.i = 0.2 * pd.i + 0.8 * pd.i1; - - // determine phase position - ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0; - - // change in phase - var delta = Math.Max(ph.i1 - ph.i, 1d); - - // adaptive alpha value - var alpha = Math.Max(fastl / delta, slowl); - - // final indicators - mama.i = alpha * (pr.i - mama.i1) + mama.i1; - fama.i = 0.5d * alpha * (mama.i - fama.i1) + fama.i1; - } - else - { - sumPr += pr.i; - pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0; - mama.i = fama.i = sumPr / (i + 1); - - if (_len == 1) - { - mamaseed = famaseed = TValue.v; - } - else - { - mamaseed = fastl * (TValue.v - mamaseed) + mamaseed; - famaseed = slowl * (TValue.v - famaseed) + famaseed; - } - } - - double _fama = (i > 5) ? fama.i : famaseed; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fama); - Fama.Add(res, update); - double _mama = (i > 5) ? mama.i : mamaseed; - res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mama); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAPE_Series.cs b/archive/Calculations/_Updated/MAPE_Series.cs deleted file mode 100644 index 0ac76546..00000000 --- a/archive/Calculations/_Updated/MAPE_Series.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAPE: Mean Absolute Percentage Error - Measures the size of the error in percentage terms - -Calculation: - MAPE = Σ(|close – SMA| / |close|) / n - -Sources: - https://en.wikipedia.org/wiki/Mean_absolute_percentage_error - -Remark: - returns infinity if any of observations is 0. - Use SMAPE or WMAPE instead to avoid division-by-zero in MAPE - - */ - -public class MAPE_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAPE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MAPE({period})"; - } - public MAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAPE_Series() : this(period: 0, useNaN: false) { } - public MAPE_Series(int period) : this(period: period, useNaN: false) { } - public MAPE_Series(TBars source) : this(source.Close, 0, false) { } - public MAPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAPE_Series(TSeries source) : this(source, 0, false) { } - public MAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _mape = 0; - for (int i = 0; i < _buffer.Count; i++) - { - _mape += (_buffer[i] != 0) ? Math.Abs(_buffer[i] - _sma) / Math.Abs(_buffer[i]) : double.PositiveInfinity; - } - _mape /= (_buffer.Count > 0) ? _buffer.Count : 1; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mape); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAX_Series.cs b/archive/Calculations/_Updated/MAX_Series.cs deleted file mode 100644 index 17aa9c71..00000000 --- a/archive/Calculations/_Updated/MAX_Series.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAX - Maximum value in the given period in the series. - If period = 0 => period = full length of the series - - */ - -public class MAX_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAX_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MAX({period})"; - } - public MAX_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAX_Series() : this(period: 0, useNaN: false) { } - public MAX_Series(int period) : this(period: period, useNaN: false) { } - public MAX_Series(TBars source) : this(source.Close, 0, false) { } - public MAX_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAX_Series(TSeries source) : this(source, 0, false) { } - public MAX_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _max = _buffer.Max(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MEDIAN_Series.cs b/archive/Calculations/_Updated/MEDIAN_Series.cs deleted file mode 100644 index e431c979..00000000 --- a/archive/Calculations/_Updated/MEDIAN_Series.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MED - Median value - Median of numbers is the middlemost value of the given set of numbers. - It separates the higher half and the lower half of a given data sample. - At least half of the observations are smaller than or equal to median - and at least half of the observations are greater than or equal to the median. - - If the number of values is odd, the middlemost observation of the sorted - list is the median of the given data. If the number of values is even, - median is the average of (n/2)th and [(n/2) + 1]th values of the sorted list. - - If period = 0 => period is max - -Sources: - https://corporatefinanceinstitute.com/resources/knowledge/other/median/ - https://en.wikipedia.org/wiki/Median - - */ - -public class MEDIAN_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MEDIAN_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MEDIAN({period})"; - } - public MEDIAN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MEDIAN_Series() : this(period: 0, useNaN: false) { } - public MEDIAN_Series(int period) : this(period: period, useNaN: false) { } - public MEDIAN_Series(TBars source) : this(source.Close, 0, false) { } - public MEDIAN_Series(TBars source, int period) : this(source.Close, period, false) { } - public MEDIAN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MEDIAN_Series(TSeries source) : this(source, 0, false) { } - public MEDIAN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - System.Collections.Generic.List _s = new(this._buffer); - _s.Sort(); - int _p1 = _s.Count / 2; - int _p2 = Math.Max(0, (_s.Count / 2) - 1); - double _med = (_s.Count % 2 != 0) ? _s[_p1] : (_s[_p1] + _s[_p2]) / 2; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _med); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MIDPOINT_Series.cs b/archive/Calculations/_Updated/MIDPOINT_Series.cs deleted file mode 100644 index ba98b3b3..00000000 --- a/archive/Calculations/_Updated/MIDPOINT_Series.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MIDPOINT: Midpoint value (max+min)/2 in the given period in the series. - If period = 0 => period = full length of the series - -Sources: - https://thefaqblog.com/what-is-the-midpoint-in-statistics/ - - */ - -public class MIDPOINT_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MIDPOINT_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MIDPOINT({period})"; - } - public MIDPOINT_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MIDPOINT_Series() : this(period: 0, useNaN: false) { } - public MIDPOINT_Series(int period) : this(period: period, useNaN: false) { } - public MIDPOINT_Series(TBars source) : this(source.Close, 0, false) { } - public MIDPOINT_Series(TBars source, int period) : this(source.Close, period, false) { } - public MIDPOINT_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MIDPOINT_Series(TSeries source) : this(source, 0, false) { } - public MIDPOINT_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _max = _buffer.Max(); - double _min = _buffer.Min(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : (_max + _min) * 0.5); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MIDPRICE_Series.cs b/archive/Calculations/_Updated/MIDPRICE_Series.cs deleted file mode 100644 index 851b23fd..00000000 --- a/archive/Calculations/_Updated/MIDPRICE_Series.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -MIDPRICE: Midpoint price (highhest high + lowest low)/2 in the given period in the series. - If period = 0 => period = full length of the series - - */ - -public class MIDPRICE_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private readonly System.Collections.Generic.List _bufferhi = new(); - private readonly System.Collections.Generic.List _bufferlo = new(); - - //core constructors - public MIDPRICE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MIDPRICE({period})"; - } - public MIDPRICE_Series(TBars source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public MIDPRICE_Series() : this(period: 2, useNaN: false) { } - public MIDPRICE_Series(int period) : this(period: period, useNaN: false) { } - public MIDPRICE_Series(TBars source) : this(source, period: 2, useNaN: false) { } - public MIDPRICE_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - BufferTrim(_bufferhi, TBar.h, _period, update); - BufferTrim(_bufferlo, TBar.l, _period, update); - double _mid = (_bufferhi.Max() + _bufferlo.Min()) * 0.5; - - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _mid); - return base.Add(res, update); - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _bufferhi.Clear(); - _bufferlo.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MIN_Series.cs b/archive/Calculations/_Updated/MIN_Series.cs deleted file mode 100644 index d3d7bb73..00000000 --- a/archive/Calculations/_Updated/MIN_Series.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MIN - Minimum value in the given period in the series. - If period = 0 => period = full length of the series - - */ - -public class MIN_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MIN_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MAX({period})"; - } - public MIN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MIN_Series() : this(period: 0, useNaN: false) { } - public MIN_Series(int period) : this(period: period, useNaN: false) { } - public MIN_Series(TBars source) : this(source.Close, 0, false) { } - public MIN_Series(TBars source, int period) : this(source.Close, period, false) { } - public MIN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MIN_Series(TSeries source) : this(source, 0, false) { } - public MIN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _max = _buffer.Min(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MSE_Series.cs b/archive/Calculations/_Updated/MSE_Series.cs deleted file mode 100644 index 34440185..00000000 --- a/archive/Calculations/_Updated/MSE_Series.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MSE: Mean Square Error - Defined as a Mean (Average) of the Square of the difference between actual and estimated values. - -Sources: - https://en.wikipedia.org/wiki/Mean_squared_error - - */ - -public class MSE_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MSE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"MSE({period})"; - } - public MSE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MSE_Series() : this(period: 0, useNaN: false) { } - public MSE_Series(int period) : this(period: period, useNaN: false) { } - public MSE_Series(TBars source) : this(source.Close, 0, false) { } - public MSE_Series(TBars source, int period) : this(source.Close, period, false) { } - public MSE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MSE_Series(TSeries source) : this(source, 0, false) { } - public MSE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _mse = 0; - for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _mse /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mse); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/OBV_Series.cs b/archive/Calculations/_Updated/OBV_Series.cs deleted file mode 100644 index 2025034e..00000000 --- a/archive/Calculations/_Updated/OBV_Series.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -OBV: On-Balance Volume - On-balance volume (OBV) is a technical trading momentum indicator that uses volume flow to predict - changes in stock price. Joseph Granville first developed the OBV metric in the 1963 book - Granville's New Key to Stock Market Profits. - - | +volume; if close > close[previous] - OBV = OBV[previous] + | 0; if close = close[previous] - | -volume; if close < close[previous] - -Sources: - https://www.investopedia.com/terms/o/onbalancevolume.asp - https://www.tradingview.com/wiki/On_Balance_Volume_(OBV) - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/on-balance-volume-obv/ - https://www.motivewave.com/studies/on_balance_volume.htm - -Note: - There is no consensus on what is the first OBV value in the series: - - TA-LIB uses the first volume: OBV[0] = volume[0] - - Skender stock library uses 0: OBV[0] = 0 - - */ - -public class OBV_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private double _lastobv, _lastlastobv; - private double _lastclose, _lastlastclose; - - //core constructors - public OBV_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"OBV({period})"; - this._lastobv = this._lastlastobv = 0; - this._lastclose = this._lastlastclose = 0; - } - public OBV_Series(TBars source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public OBV_Series() : this(period: 2, useNaN: false) { } - public OBV_Series(int period) : this(period: period, useNaN: false) { } - public OBV_Series(TBars source) : this(source, period: 2, useNaN: false) { } - public OBV_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - - if (update) - { - this._lastobv = this._lastlastobv; - this._lastclose = this._lastlastclose; - } - - double _obv = this._lastobv; - if (TBar.c > this._lastclose) { _obv += TBar.v; } - if (TBar.c < this._lastclose) { _obv -= TBar.v; } - - this._lastlastobv = this._lastobv; - this._lastobv = _obv; - - this._lastlastclose = this._lastclose; - this._lastclose = TBar.c; - - var res = (TBar.t, (this.Count < this._period && this._NaN) ? double.NaN : _obv); - return base.Add(res, update); - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - this._lastobv = this._lastlastobv = 0; - this._lastclose = this._lastlastclose = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/RMA_Series.cs b/archive/Calculations/_Updated/RMA_Series.cs deleted file mode 100644 index a1b94259..00000000 --- a/archive/Calculations/_Updated/RMA_Series.cs +++ /dev/null @@ -1,133 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -RMA: wildeR Moving Average - J. Welles Wilder introduced RMA as an alternative to EMA. RMA's weight (k) is - set as 1/period, giving less weight to the new data compared to EMA. - -Sources: - https://archive.org/details/newconceptsintec00wild/page/23/mode/2up - https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing - https://www.incrediblecharts.com/indicators/wilder_moving_average.php - -Issues: - Pandas-TA library calculates RMA using straight Exponential Weighted Mean: - pandas.ewm().mean() and returns incorrect first (period) of bars compared to - published formula. This implementation passess the validation test in Wilder's book. - - */ - -public class RMA_Series : TSeries -{ - private double _k; - private double _lastrma, _oldrma; - private double _sum, _oldsum; - private readonly bool _useSMA; - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructor - public RMA_Series(int period, bool useNaN, bool useSMA) - { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"RMA({period})"; - _k = 1.0 / (double)(this._period); - _len = 0; - _sum = _oldsum = _lastrma = _oldrma = 0; - } - //generic constructors (source) - - public RMA_Series() : this(0, false, true) { } - public RMA_Series(int period) : this(period, false, true) { } - public RMA_Series(TBars source) : this(source.Close, 0, false) { } - public RMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public RMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public RMA_Series(TSeries source, int period) : this(source, period, false, true) { } - public RMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - public RMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (update) - { - _lastrma = _oldrma; - _sum = _oldsum; - } - else - { - _oldrma = _lastrma; - _oldsum = _sum; - _len++; - } - - double _rma = 0; - if (_period == 0) - { - _k = 1.0 / (double)(this._len); - } - - if (Count == 0) - { - _rma = _sum = TValue.v; - - } - else if (_len <= _period && _useSMA && _period != 0) - { - _sum += TValue.v; - if (_period != 0 && _len > _period) - { - _sum -= _data[Count - _period - (update ? 1 : 0)].v; - } - _rma = _sum / Math.Min(_len, _period); - } - else - { - _rma = _k * (TValue.v - _lastrma) + _lastrma; - } - - _lastrma = double.IsNaN(_rma) ? _lastrma : _rma; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rma); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _sum = _oldsum = _lastrma = _oldrma = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/RSI_Series.cs b/archive/Calculations/_Updated/RSI_Series.cs deleted file mode 100644 index 6a037b28..00000000 --- a/archive/Calculations/_Updated/RSI_Series.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -RSI: Relative Strength Index - Created by J. Welles Wilder, the Relative Strength Index measures strength - of the winning/losing streak over N lookback periods on a scale of 0 to 100, - to depict overbought and oversold conditions. - -Sources: - https://www.investopedia.com/terms/r/rsi.asp - - */ - -public class RSI_Series : TSeries -{ - private readonly System.Collections.Generic.List _gain = new(); - private readonly System.Collections.Generic.List _loss = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _avgGain, _avgLoss, _lastValue; - private double _avgGain_o, _avgLoss_o, _lastValue_o; - private int i; - - //core constructors - public RSI_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"RSI({period})"; - i = 0; - } - public RSI_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public RSI_Series() : this(period: 0, useNaN: false) { } - public RSI_Series(int period) : this(period: period, useNaN: false) { } - public RSI_Series(TBars source) : this(source.Close, 0, false) { } - public RSI_Series(TBars source, int period) : this(source.Close, period, false) { } - public RSI_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public RSI_Series(TSeries source) : this(source, 0, false) { } - public RSI_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - - double _rsi = 0; - if (update) - { - _lastValue = _lastValue_o; - _avgGain = _avgGain_o; - _avgLoss = _avgLoss_o; - } - else - { - _lastValue_o = _lastValue; - _avgGain_o = _avgGain; - _avgLoss_o = _avgLoss; - } - - if (i == 0) { _lastValue = TValue.v; } - - double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0; - BufferTrim(_gain, _gainval, _period, update); - double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0; - BufferTrim(_loss, _lossval, _period, update); - _lastValue = TValue.v; - - // calculate RSI - if (i > _period && _period != 0) - { - _avgGain = ((_avgGain * (_period - 1)) + _gain[^1]) / _period; - _avgLoss = ((_avgLoss * (_period - 1)) + _loss[^1]) / _period; - if (_avgLoss > 0) - { - double rs = _avgGain / _avgLoss; - _rsi = 100 - (100 / (1 + rs)); - } - else { _rsi = 100; } - } - // initialize average gain - else - { - double _sumGain = 0; - for (int p = 0; p < _gain.Count; p++) { _sumGain += _gain[p]; } - double _sumLoss = 0; - for (int p = 0; p < _loss.Count; p++) { _sumLoss += _loss[p]; } - - _avgGain = _sumGain / _gain.Count; - _avgLoss = _sumLoss / _loss.Count; - - _rsi = (_avgLoss > 0) ? 100 - (100 / (1 + (_avgGain / _avgLoss))) : 100; - } - if (!update) { i++; } - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rsi); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - i = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SDEV_Series.cs b/archive/Calculations/_Updated/SDEV_Series.cs deleted file mode 100644 index 88fe0ce1..00000000 --- a/archive/Calculations/_Updated/SDEV_Series.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SDEV: Population Standard Deviation - Population Standard Deviation is the square root of the biased variance, also knons as - Uncorrected Sample Standard Deviation - -Sources: - https://en.wikipedia.org/wiki/Standard_deviation#Uncorrected_sample_standard_deviation - -Remark: - SDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation. - For unbiased version that uses Bessel's correction, use SDEV instead. - - */ - -public class SDEV_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SDEV_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"SDEV({period})"; - } - public SDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SDEV_Series() : this(period: 0, useNaN: false) { } - public SDEV_Series(int period) : this(period: period, useNaN: false) { } - public SDEV_Series(TBars source) : this(source.Close, 0, false) { } - public SDEV_Series(TBars source, int period) : this(source.Close, period, false) { } - public SDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SDEV_Series(TSeries source) : this(source, 0, false) { } - public SDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _var = 0; - for (int i = 0; i < _buffer.Count; i++) { _var += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _var /= this._buffer.Count; - double _sdev = Math.Sqrt(_var); - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sdev); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SLOPE_Series.cs b/archive/Calculations/_Updated/SLOPE_Series.cs deleted file mode 100644 index c47b8650..00000000 --- a/archive/Calculations/_Updated/SLOPE_Series.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SLOPE: Slope of linear regression (using Least Square Method) - Linear Regression provides a slope of a straight line that is the best approximation of the given set of data. - The method of least squares is a standard approach in linear regression analysis to approximate the solution - by minimizing the sum of the squares of the residuals made in the results of each individual equation. - -Additional outputs provided by LINREG: - .Intercept - y-intercept point of the best fit line - .RSquared - R-Squared (R²), Coefficient of Determination - .StdDev - Standard Deviation of data over given periods - - y = Slope * x + Intercept - -Sources: - https://en.wikipedia.org/wiki/Least_squares - - */ - -public class SLOPE_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly TSeries p_Intercept = new(); - private readonly TSeries p_RSquared = new(); - private readonly TSeries p_StdDev = new(); - private readonly System.Collections.Generic.List _buffer = new(); - public TSeries Intercept => p_Intercept; - public TSeries RSquared => p_RSquared; - public TSeries StdDev => p_StdDev; - //core constructors - public SLOPE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"SLOPE({period})"; - } - public SLOPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SLOPE_Series() : this(period: 0, useNaN: false) { } - public SLOPE_Series(int period) : this(period: period, useNaN: false) { } - public SLOPE_Series(TBars source) : this(source.Close, 0, false) { } - public SLOPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public SLOPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SLOPE_Series(TSeries source) : this(source, 0, false) { } - public SLOPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - int _len = this._buffer.Count; - - // get averages for period - double sumX = 0; - double sumY = 0; - - for (int p = 0; p < _len; p++) - { - sumX += this.Count - _len + 2 + p; - sumY += _buffer[p]; - } - double avgX = sumX / _len; - double avgY = sumY / _len; - - // least squares method - double sumSqX = 0; - double sumSqY = 0; - double sumSqXY = 0; - - for (int p = 0; p < _len; p++) - { - double devX = this.Count - _len + 2 + p - avgX; - double devY = _buffer[p] - avgY; - - sumSqX += devX * devX; - sumSqY += devY * devY; - sumSqXY += devX * devY; - } - - double _slope = sumSqXY / sumSqX; - double _intercept = avgY - (_slope * avgX); - - // calculate Standard Deviation and R-Squared - double stdDevX = Math.Sqrt(sumSqX / _len); - double stdDevY = Math.Sqrt(sumSqY / _len); - double _StdDev = stdDevY; - - double arrr = (stdDevX * stdDevY != 0) ? sumSqXY / (stdDevX * stdDevY) / _len : 0; - double _RSquared = arrr * arrr; - - var ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _intercept); - p_Intercept.Add(ret, update); - - ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _StdDev); - p_StdDev.Add(ret, update); - - ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _RSquared); - p_RSquared.Add(ret, update); - - ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _slope); - return base.Add(ret, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SMAPE_Series.cs b/archive/Calculations/_Updated/SMAPE_Series.cs deleted file mode 100644 index a2e65e05..00000000 --- a/archive/Calculations/_Updated/SMAPE_Series.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SMAPE: Symmetric Mean Absolute Percentage Error - Measures the size of the error in percentage terms - -Sources: - https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error - - */ - -public class SMAPE_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SMAPE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"SMAPE({period})"; - } - public SMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SMAPE_Series() : this(period: 0, useNaN: false) { } - public SMAPE_Series(int period) : this(period: period, useNaN: false) { } - public SMAPE_Series(TBars source) : this(source.Close, 0, false) { } - public SMAPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public SMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SMAPE_Series(TSeries source) : this(source, 0, false) { } - public SMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - double _smape = 0; - for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); } - _smape /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smape); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SMA_Series.cs b/archive/Calculations/_Updated/SMA_Series.cs deleted file mode 100644 index e4c7b9e6..00000000 --- a/archive/Calculations/_Updated/SMA_Series.cs +++ /dev/null @@ -1,112 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SMA: Simple Moving Average - The weights are equally distributed across the period, resulting in a mean() of - the data within the period - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/ - https://stats.stackexchange.com/a/24739 - -Remark: - This calc doesn't use LINQ or SUM() or any of (slow) iterative methods. It is not as fast as TA-LIB - implementation, but it does allow incremental additions of inputs and real-time calculations of SMA() - - */ -public class SMA_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - - private double _sum, _oldsum; - private readonly int _period; - private readonly TSeries _data; - protected readonly bool _NaN; - - //core constructor - public SMA_Series(int period, bool useNaN) - { - _period = Math.Max(0, period); - _NaN = useNaN; - Name = $"SMA({period})"; - _sum = _oldsum = 0; - } - public SMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SMA_Series() : this(0, false) { } - public SMA_Series(int period) : this(period, false) { } - public SMA_Series(TBars source) : this(source.Close, 0, false) { } - public SMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public SMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SMA_Series(TSeries source) : this(source, 0, false) { } - public SMA_Series(TSeries source, int period) : this(source, period, false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return (TValue.t, double.NaN); - } - else - { - if (update && _buffer.Count > 0) - { - _sum -= _buffer[^1]; - _buffer[^1] = TValue.v; - _oldsum = _sum; - } - else - { - _buffer.Add(TValue.v); - _oldsum = _sum; - } - - _sum += TValue.v; - if (_period != 0 && _buffer.Count > _period) - { - _sum -= _buffer[0]; - _buffer.RemoveAt(0); - } - } - - double _div = _period == 0 ? _buffer.Count : Math.Min(_buffer.Count, _period); - var _sma = _sum / _div; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sma); - return base.Add(res, update); - } - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - - //reset calculation - public override void Reset() - { - _sum = _oldsum = 0; - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SMMA_Series.cs b/archive/Calculations/_Updated/SMMA_Series.cs deleted file mode 100644 index cfba2a57..00000000 --- a/archive/Calculations/_Updated/SMMA_Series.cs +++ /dev/null @@ -1,105 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -SMMA: Smoothed Moving Average - The Smoothed Moving Average (SMMA) is a combination of a SMA and an EMA. It gives the recent prices - an equal weighting as the historic prices as it takes all available price data into account. - The main advantage of a smoothed moving average is that it removes short-term fluctuations. - - SMMA(i) = (SMMA-1*(N-1) + CLOSE (i)) / N - -Sources: - https://blog.earn2trade.com/smoothed-moving-average - https://guide.traderevolution.com/traderevolution/mobile-applications/phone/android/technical-indicators/moving-averages/smma-smoothed-moving-average - https://www.chartmill.com/documentation/technical-analysis-indicators/217-MOVING-AVERAGES-%7C-The-Smoothed-Moving-Average-%28SMMA%29 - - */ - -public class SMMA_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _lastsmma, _lastlastsmma; - - //core constructors - public SMMA_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"SMMA({period})"; - } - public SMMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SMMA_Series() : this(period: 0, useNaN: false) { } - public SMMA_Series(int period) : this(period: period, useNaN: false) { } - public SMMA_Series(TBars source) : this(source.Close, 0, false) { } - public SMMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public SMMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SMMA_Series(TSeries source) : this(source, 0, false) { } - public SMMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, double.NaN), update); - } - - double _smma = 0; - if (update) { this._lastsmma = this._lastlastsmma; } - - if (this.Count < this._period) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - _smma = _buffer.Average(); - } - else - { - _smma = ((_lastsmma * (_period - 1)) + TValue.v) / _period; - } - - this._lastlastsmma = this._lastsmma; - this._lastsmma = _smma; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - this._lastsmma = this._lastlastsmma = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SSDEV_Series.cs b/archive/Calculations/_Updated/SSDEV_Series.cs deleted file mode 100644 index ae2a10de..00000000 --- a/archive/Calculations/_Updated/SSDEV_Series.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SSDEV: (Corrected) Sample Standard Deviation - Sample Standard Deviaton uses Bessel's correction to correct the bias in the variance. - -Sources: - https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation - Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction - -Remark: - SSDEV (Sample Standard Deviation) is also known as a unbiased/corrected Standard Deviation. - For a population/biased/uncorrected Standard Deviation, use PSDEV instead - - */ - -public class SSDEV_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SSDEV_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"SSDEV({period})"; - } - public SSDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SSDEV_Series() : this(period: 0, useNaN: false) { } - public SSDEV_Series(int period) : this(period: period, useNaN: false) { } - public SSDEV_Series(TBars source) : this(source.Close, 0, false) { } - public SSDEV_Series(TBars source, int period) : this(source.Close, period, false) { } - public SSDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SSDEV_Series(TSeries source) : this(source, 0, false) { } - public SSDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _svar = 0; - for (int i = 0; i < this._buffer.Count; i++) { _svar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _svar /= (_buffer.Count > 1) ? _buffer.Count - 1 : 1; // Bessel's correction - double _ssdev = Math.Sqrt(_svar); - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ssdev); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SVAR_Series.cs b/archive/Calculations/_Updated/SVAR_Series.cs deleted file mode 100644 index 3438c9d5..00000000 --- a/archive/Calculations/_Updated/SVAR_Series.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -VAR: Population Variance - Population variance without Bessel's correction - -Sources: - https://en.wikipedia.org/wiki/Variance - Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction - -Remark: - VAR (Population Variance) is also known as a biased Sample Variance. For unbiased - sample variance use SVAR instead. - - */ - -public class SVAR_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SVAR_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"SVAR({period})"; - } - public SVAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SVAR_Series() : this(period: 0, useNaN: false) { } - public SVAR_Series(int period) : this(period: period, useNaN: false) { } - public SVAR_Series(TBars source) : this(source.Close, 0, false) { } - public SVAR_Series(TBars source, int period) : this(source.Close, period, false) { } - public SVAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SVAR_Series(TSeries source) : this(source, 0, false) { } - public SVAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _svar = 0; - for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); } - _svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _svar); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/T3_Series.cs b/archive/Calculations/_Updated/T3_Series.cs deleted file mode 100644 index b203f818..00000000 --- a/archive/Calculations/_Updated/T3_Series.cs +++ /dev/null @@ -1,173 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Numerics; - -/* -T3: Tillson T3 Moving Average - Tim Tillson described it in "Technical Analysis of Stocks and Commodities", January 1998 in the - article "Better Moving Averages". Tillson’s moving average becomes a popular indicator of - technical analysis as it gets less lag with the price chart and its curve is considerably smoother. - -Sources: - https://technicalindicators.net/indicators-technical-analysis/150-t3-moving-average - http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/ - */ - -public class T3_Series : TSeries -{ - private readonly double _k, _k1m, _c1, _c2, _c3, _c4; - private readonly System.Collections.Generic.List _buffer1 = new(); - private readonly System.Collections.Generic.List _buffer2 = new(); - private readonly System.Collections.Generic.List _buffer3 = new(); - private readonly System.Collections.Generic.List _buffer4 = new(); - private readonly System.Collections.Generic.List _buffer5 = new(); - private readonly System.Collections.Generic.List _buffer6 = new(); - private readonly bool _useSMA; - private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6; - private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6; - protected int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) - { - _period = period; - _len = 0; - _NaN = useNaN; - Name = $"T3({period})"; - _useSMA = useSMA; - double _a = vfactor; //0.7; //0.618 - _c1 = -_a * _a * _a; - _c2 = 3 * _a * _a + 3 * _a * _a * _a; - _c3 = -6 * _a * _a - 3 * _a - 3 * _a * _a * _a; - _c4 = 1 + 3 * _a + _a * _a * _a + 3 * _a * _a; - - _k = 2.0 / (_period + 1); - _k1m = 1.0 - _k; - _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0; - } - public T3_Series(TSeries source, int period, double vfactor, bool useSMA, bool useNaN) : this(period, vfactor, useSMA, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public T3_Series() : this(period: 0, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(int period) : this(period: period, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TBars source) : this(source.Close, 0, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TBars source, int period) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TBars source, int period, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { } - public T3_Series(TBars source, int period, double vfactor, bool useNaN) : this(source.Close, period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { } - public T3_Series(TBars source, int period, bool useSMA, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: useSMA, useNaN: useNaN) { } - public T3_Series(TSeries source) : this(source, 0, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TSeries source, int period) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { } - public T3_Series(TSeries source, int period, double vfactor) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: false) { } - public T3_Series(TSeries source, int period, double vfactor, bool useNaN) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6; - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - - if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; _lastema4 = _llastema4; _lastema5 = _llastema5; _lastema6 = _llastema6; } - else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _llastema4 = _lastema4; _llastema5 = _lastema5; _llastema6 = _lastema6; } - - if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; } - - - if ((_len < _period) && _useSMA) - { - BufferTrim(_buffer1, TValue.v, _period, update); - _ema1 = 0; - for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; } - _ema1 /= _buffer1.Count; - - BufferTrim(_buffer2, _ema1, _period, update); - _ema2 = 0; - for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; } - _ema2 /= _buffer2.Count; - - BufferTrim(_buffer3, _ema2, _period, update); - _ema3 = 0; - for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; } - _ema3 /= _buffer3.Count; - - BufferTrim(_buffer4, _ema3, _period, update); - _ema4 = 0; - for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; } - _ema4 /= _buffer4.Count; - - BufferTrim(_buffer5, _ema4, _period, update); - _ema5 = 0; - for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; } - _ema5 /= _buffer5.Count; - - BufferTrim(_buffer6, _ema5, _period, update); - _ema6 = 0; - for (int i = 0; i < _buffer6.Count; i++) { _ema6 += _buffer6[i]; } - _ema6 /= _buffer6.Count; - } - else - { - _ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m); - _ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m); - _ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m); - _ema4 = (_ema3 * this._k) + (this._lastema4 * this._k1m); - _ema5 = (_ema4 * this._k) + (this._lastema5 * this._k1m); - _ema6 = (_ema5 * this._k) + (this._lastema6 * this._k1m); - } - _len++; - _lastema1 = _ema1; - _lastema2 = _ema2; - _lastema3 = _ema3; - _lastema4 = _ema4; - _lastema5 = _ema5; - _lastema6 = _ema6; - - double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _T3); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0; - _buffer1.Clear(); - _buffer2.Clear(); - _buffer3.Clear(); - _buffer4.Clear(); - _buffer5.Clear(); - _buffer6.Clear(); - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TBars.cs b/archive/Calculations/_Updated/TBars.cs deleted file mode 100644 index 2d72df30..00000000 --- a/archive/Calculations/_Updated/TBars.cs +++ /dev/null @@ -1,153 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -TBars class - includes all series for common data used in indicators and other calculations. - Has a bit limited overloading and casting (compared to TSeries) - Includes Select(int) method to simplify choosing the most optimal data source for indicators - Includes the most basic pricing calcs: HL2, OC2, OHL3, HLC3, OHLC4, HLCC4 - (it is 'cheaper' to calculate them once during data capture than each time during data analysis) - - */ - -public class TBars : System.Collections.Generic.List<(DateTime t, double o, double h, double l, double c, double v)> -{ - public string Name { get; set; } - private readonly TSeries _open = new("open"); - private readonly TSeries _high = new("high"); - private readonly TSeries _low = new("low"); - private readonly TSeries _close = new("close"); - private readonly TSeries _volume = new("volume"); - private readonly TSeries _hl2 = new("HL2"); - private readonly TSeries _oc2 = new("OC2"); - private readonly TSeries _ohl3 = new("OHL3"); - private readonly TSeries _hlc3 = new("HLC3"); - private readonly TSeries _ohlc4 = new("OHLC4"); - private readonly TSeries _hlcc4 = new("HLCC4"); - - public TSeries Open => this._open; - public TSeries High => this._high; - public TSeries Low => this._low; - public TSeries Close => this._close; - public TSeries Volume => this._volume; - public TSeries HL2 => this._hl2; - public TSeries OC2 => this._oc2; - public TSeries OHL3 => this._ohl3; - public TSeries HLC3 => this._hlc3; - public TSeries OHLC4 => this._ohlc4; - public TSeries HLCC4 => this._hlcc4; - - public TBars() { } - - public TBars(string Name) - { - this.Name = Name; - } - - public (DateTime t, double o, double h, double l, double c, double v) Last => this[^1]; - public TBars Tail(int count = 10) - { - TBars outBars = new(); - if (count > this.Count) { count = this.Count; } - for (int i = this.Count - count; i < this.Count; i++) { outBars.Add(this[i]); } - return outBars; - } - public TSeries Select(int source) - { - return source switch - { - 0 => _open, - 1 => _high, - 2 => _low, - 3 => _close, - 4 => _hl2, - 5 => _oc2, - 6 => _ohl3, - 7 => _hlc3, - 8 => _ohlc4, - _ => _hlcc4, - }; - } - public static string SelectStr(int source) - { - return source switch - { - 0 => "Open", - 1 => "High", - 2 => "Low", - 3 => "Close", - 4 => "HL2", - 5 => "OC2", - 6 => "OHL3", - 7 => "HLC3", - 8 => "OHLC4", - _ => "HLCC4", - }; - } - - public virtual (DateTime t, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) => - Add((t: (this.Count == 0) ? DateTime.Today : this[^1].t.AddDays(1), p.o, p.h, p.l, p.c, p.v), update); - - public virtual (DateTime t, double v) Add(double o, double h, double l, double c, double v, bool update = false) => - Add((o, h, l, c, v), update); - - public virtual (DateTime t, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) => - this.Add((t, o, h, l, c, v), update); - - public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - if (update) { this[^1] = TBar; } else { base.Add(TBar); } - - _open.Add((TBar.t, TBar.o), update); - _high.Add((TBar.t, TBar.h), update); - _low.Add((TBar.t, TBar.l), update); - _close.Add((TBar.t, TBar.c), update); - _volume.Add((TBar.t, TBar.v), update); - _hl2.Add((TBar.t, (TBar.h + TBar.l) * 0.5), update); - _oc2.Add((TBar.t, (TBar.o + TBar.c) * 0.5), update); - _ohl3.Add((TBar.t, (TBar.o + TBar.h + TBar.l) * 0.333333333333333), update); - _hlc3.Add((TBar.t, (TBar.h + TBar.l + TBar.c) * 0.333333333333333), update); - _ohlc4.Add((TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25), update); - _hlcc4.Add((TBar.t, (TBar.h + TBar.l + TBar.c + TBar.c) * 0.25), update); - - this.OnEvent(update); - return (TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25); - } - - public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); - public event NewDataEventHandler Pub; - protected virtual void OnEvent(bool update = false) - { - if (Pub != null && Pub.Target != this) - { - Pub(this, new TSeriesEventArgs { update = update }); - } - } - - public void Sub(object source, TSeriesEventArgs e) - { - TBars ss = (TBars)source; if (ss.Count > 1) - { - for (int i = 0; i < ss.Count; i++) { this.Add(ss[i]); } - } - else - { - this.Add(ss[^1], e.update); - } - } - - /// common helpers - public static void BufferTrim(System.Collections.Generic.List buffer, double value, int period, bool update) - { - if (!update) - { - buffer.Add(value); - if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); } - return; - } - buffer[^1] = value; - } - public virtual void Reset() - { - } -} diff --git a/archive/Calculations/_Updated/TEMA_Series.cs b/archive/Calculations/_Updated/TEMA_Series.cs deleted file mode 100644 index f1720fad..00000000 --- a/archive/Calculations/_Updated/TEMA_Series.cs +++ /dev/null @@ -1,134 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -TEMA: Triple Exponential Moving Average - TEMA uses EMA(EMA(EMA())) to calculate less laggy Exponential moving average. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/ - -Remark: - ema1 = EMA(close, length) - ema2 = EMA(ema1, length) - ema3 = EMA(ema2, length) - TEMA = 3 * (ema1 - ema2) + ema3 - - */ - -public class TEMA_Series : TSeries -{ - private double _k; - private double _sum, _oldsum; - private double _lastema1, _oldema1, _lastema2, _oldema2, _lastema3, _oldema3; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructor - public TEMA_Series(int period, bool useNaN, bool useSMA) - { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"TEMA({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _sum = _oldsum = _lastema1 = _lastema2 = _lastema3 = 0; - } - public TEMA_Series() : this(0, false, true) { } - public TEMA_Series(int period) : this(period, false, true) { } - public TEMA_Series(TBars source) : this(source.Close, 0, false) { } - public TEMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public TEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public TEMA_Series(TSeries source, int period) : this(source, period, false, true) { } - public TEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - public TEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (update) - { - _lastema1 = _oldema1; - _lastema2 = _oldema2; - _lastema3 = _oldema3; - _sum = _oldsum; - } - else - { - _oldema1 = _lastema1; - _oldema2 = _lastema2; - _oldema3 = _lastema3; - _oldsum = _sum; - _len++; - } - - if (_period == 0) { _k = 2.0 / (_len + 1); } - - double _ema1, _ema2, _ema3, _tema; - if (this.Count == 0) - { - _ema1 = _ema2 = _ema3 = _sum = TValue.v; - } - else if (_len <= _period && _useSMA && _period != 0) - { - _sum += TValue.v; - _ema1 = _sum / Math.Min(_len, _period); - _ema2 = _ema1; - _ema3 = _ema2; - } - else - { - _ema1 = (TValue.v - _lastema1) * _k + _lastema1; - _ema2 = (_ema1 - _lastema2) * _k + _lastema2; - _ema3 = (_ema2 - _lastema3) * _k + _lastema3; - } - - _tema = (3 * (_ema1 - _ema2)) + _ema3; - - _lastema1 = Double.IsNaN(_ema1) ? _lastema1 : _ema1; - _lastema2 = Double.IsNaN(_ema2) ? _lastema2 : _ema2; - _lastema3 = Double.IsNaN(_ema3) ? _lastema3 : _ema3; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _tema); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _sum = _oldsum = _lastema1 = _lastema2 = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TRIMA_Series.cs b/archive/Calculations/_Updated/TRIMA_Series.cs deleted file mode 100644 index 3c972143..00000000 --- a/archive/Calculations/_Updated/TRIMA_Series.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -TRIMA: Triangular Moving Average - A weighted moving average where the shape of the weights are triangular and the greatest - weight is in the middle of the period, - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/ - -Remark: - trima = sma(sma(signal, n/2), n/2) - - */ - -public class TRIMA_Series : TSeries -{ - private readonly int _p1a, _p1b; - private readonly SMA_Series sma, trima; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public TRIMA_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"xMA({period})"; - _p1a = (int)Math.Floor((period * 0.5) + 1); - _p1b = (int)Math.Ceiling(0.5 * period); - sma = new(_p1a); - trima = new(_p1b); - - } - public TRIMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public TRIMA_Series() : this(period: 0, useNaN: false) { } - public TRIMA_Series(int period) : this(period: period, useNaN: false) { } - public TRIMA_Series(TBars source) : this(source.Close, 0, false) { } - public TRIMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public TRIMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public TRIMA_Series(TSeries source) : this(source, 0, false) { } - public TRIMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - - var _sma = sma.Add(TValue, update); - var _trima = trima.Add(_sma, update); - - var res = (_trima.t, Count < _period - 1 && _NaN ? double.NaN : _trima.v); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - sma.Reset(); - trima.Reset(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TRIX_Series.cs b/archive/Calculations/_Updated/TRIX_Series.cs deleted file mode 100644 index 490eeccb..00000000 --- a/archive/Calculations/_Updated/TRIX_Series.cs +++ /dev/null @@ -1,132 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -TRIX: Triple Exponential Average Oscillator - Developed by Jack Hutson in the early 1980s, the triple exponential average (TRIX) - has become a popular technical analysis tool to aid chartists in spotting diversions - and directional cues in stock trading patterns. - -Sources: - https://www.investopedia.com/terms/t/trix.asp - - */ - -public class TRIX_Series : TSeries -{ - private readonly double _k; - private readonly System.Collections.Generic.List _buffer1 = new(); - private readonly System.Collections.Generic.List _buffer2 = new(); - private readonly System.Collections.Generic.List _buffer3 = new(); - private double _lastema1, _lastema2, _lastema3; - private double _llastema1, _llastema2, _llastema3; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - - public TRIX_Series(int period, bool useNaN, bool useSMA) - { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"TRIX({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0; - } - public TRIX_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public TRIX_Series() : this(0, false, true) { } - public TRIX_Series(int period) : this(period, false, true) { } - public TRIX_Series(TBars source) : this(source.Close, 0, false) { } - public TRIX_Series(TBars source, int period) : this(source.Close, period, false) { } - public TRIX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public TRIX_Series(TSeries source, int period) : this(source, period, false, true) { } - public TRIX_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, Double.NaN), update); - } - if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; } - if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; } - else - { - _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _len++; - } - - double _ema1, _ema2, _ema3; - if ((this.Count < _period) && _useSMA) - { - BufferTrim(_buffer1, TValue.v, _period, update); - _ema1 = 0; - for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; } - _ema1 /= _buffer1.Count; - - BufferTrim(_buffer2, _ema1, _period, update); - _ema2 = 0; - for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; } - _ema2 /= _buffer2.Count; - - BufferTrim(_buffer3, _ema2, _period, update); - _ema3 = 0; - for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; } - _ema3 /= _buffer3.Count; - } - else - { - _ema1 = (TValue.v - _lastema1) * _k + _lastema1; - _ema2 = (_ema1 - _lastema2) * _k + _lastema2; - _ema3 = (_ema2 - _lastema3) * _k + _lastema3; - } - double _trix = 100 * (_ema3 - _lastema3) / _lastema3; - _lastema1 = _ema1; - _lastema2 = _ema2; - _lastema3 = _ema3; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _trix); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _len = 0; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TR_Series.cs b/archive/Calculations/_Updated/TR_Series.cs deleted file mode 100644 index 49d942ce..00000000 --- a/archive/Calculations/_Updated/TR_Series.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -TR: True Range - True Range was introduced by J. Welles Wilder in his book New Concepts in Technical Trading Systems. - It measures the daily range plus any gap from the closing price of the preceding day. - -Calculation: - d1 = ABS(High - Low) - d2 = ABS(High - Previous close) - d3 = ABS(Previous close - Low) - TR = MAX(d1,d2,d3) - -Sources: - https://www.macroption.com/true-range/ - - */ - -public class TR_Series : TSeries -{ - protected readonly TBars _data; - private double _cm1, _cm1_o; - - //core constructors - public TR_Series() - { - Name = $"TR()"; - _cm1 = _cm1_o = double.NaN; - } - public TR_Series(TBars source) - { - _data = source; - Name = $"TR({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _cm1 = _cm1_o = double.NaN; - _data.Pub += Sub; - Add(data: _data); - } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - - if (update) - { - _cm1 = _cm1_o; - } - else - { - _cm1_o = _cm1; - } - - if (_cm1 is double.NaN) - { - _cm1 = TBar.c; - } - - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - _cm1 = TBar.c; - var ret = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - return base.Add(ret, update); - - } - - public new void Add(TBars data) - { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _cm1 = _cm1_o = double.NaN; - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TSeries.cs b/archive/Calculations/_Updated/TSeries.cs deleted file mode 100644 index 4763e6c3..00000000 --- a/archive/Calculations/_Updated/TSeries.cs +++ /dev/null @@ -1,137 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data; -using System.Linq; - -/* -TSeries is the cornerstone of all QuanTAlib classes. - TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads - and other helpers that simplify usage of library. - Think of TSeries as an equivalent of Numpy array. - - - includes Length property (to mimic array's method) - - includes publishing and subscribing methods that attach to events - - */ -public class TSeriesEventArgs : EventArgs -{ - public bool update { get; set; } -} - -public class TSeries : List<(DateTime t, double v)> -{ - private readonly (DateTime t, double v) Default = (DateTime.MinValue, double.NaN); - public IEnumerable t => this.Select(item => item.t); - public IEnumerable v => this.Select(item => item.v); - public (DateTime t, double v) Last => Count > 0 ? this[^1] : Default; - - public int Length => Count; - public string Name { get; set; } - public int Keep = 0; - - public TSeries() - { - this.Name = "data"; - } - - public TSeries(string Name) - { - this.Name = Name; - } - - public virtual (DateTime t, double v) Add(double v, bool update = false) - { - return Add((t: Count == 0 ? DateTime.Today : this[^1].t.AddDays(1), v), update); - } - - public virtual (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (update) - { - this[^1] = TValue; - } - else - { - base.Add(TValue); - } - - OnEvent(update); - return TValue; - } - - public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) - { - if (update) - { - this[this.Count - 1] = (TBar.t, TBar.c); - } - else - { - base.Add((TBar.t, TBar.c)); - } - - OnEvent(update); - return (TBar.t, TBar.c); - } - - public virtual (DateTime t, double v) Add(TSeries data) - { - foreach (var item in data) { Add(item); } - return data.Last; - } - - public virtual (DateTime t, double v) Add(TBars data) - { - foreach (var item in data) { Add(item.c, false); } - return (data.Last.t, data.Last.c); - } - - public void Sub(object source, TSeriesEventArgs e) - { - var data = (TSeries)source; - if (data == null) { return; } - foreach (var item in data) { Add(item); } - } - - public delegate void NewEventHandler(object source, TSeriesEventArgs args); - - public event NewEventHandler Pub; - - protected virtual void OnEvent(bool update = false) - { - if (Keep > 0) - { - TrimToSize(keep: Keep); - } - Pub?.Invoke(this, new TSeriesEventArgs { update = update }); - } - - /// common helpers - public static void BufferTrim(List buffer, double value, int period, bool update) - { - if (!update) - { - buffer.Add(value); - if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); } - return; - } - buffer[^1] = value; - } - public virtual void Reset() - { - } - - public void TrimToSize(int keep) - { - if (keep >= this.Count) - { - return; // No need to trim if the series is already smaller than or equal to n - } - - // Remove elements from the beginning of the list - int elementsToRemove = this.Count - keep; - RemoveRange(0, elementsToRemove); - } -} diff --git a/archive/Calculations/_Updated/VAR_Series.cs b/archive/Calculations/_Updated/VAR_Series.cs deleted file mode 100644 index 4f03e12d..00000000 --- a/archive/Calculations/_Updated/VAR_Series.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -VAR: Population Variance - Population variance without Bessel's correction - -Sources: - https://en.wikipedia.org/wiki/Variance - Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction - -Remark: - VAR (Population Variance) is also known as a biased Sample Variance. For unbiased - sample variance use SVAR instead. - - */ - -public class VAR_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public VAR_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"VAR({period})"; - } - public VAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public VAR_Series() : this(period: 0, useNaN: false) { } - public VAR_Series(int period) : this(period: period, useNaN: false) { } - public VAR_Series(TBars source) : this(source.Close, 0, false) { } - public VAR_Series(TBars source, int period) : this(source.Close, period, false) { } - public VAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public VAR_Series(TSeries source) : this(source, 0, false) { } - public VAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _pvar = 0; - for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _pvar /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pvar); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/WMAPE_Series.cs b/archive/Calculations/_Updated/WMAPE_Series.cs deleted file mode 100644 index 55935b85..00000000 --- a/archive/Calculations/_Updated/WMAPE_Series.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -WMAPE: Weighted Mean Absolute Percentage Error - Measures the size of the error in percentage terms. Improves problems with MAPE - when there are zero or close-to-zero values because there would be a division by zero - or values of MAPE tending to infinity. - -Sources: - https://en.wikipedia.org/wiki/WMAPE - - */ - -public class WMAPE_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public WMAPE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"WMAPE({period})"; - } - public WMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public WMAPE_Series() : this(period: 0, useNaN: false) { } - public WMAPE_Series(int period) : this(period: period, useNaN: false) { } - public WMAPE_Series(TBars source) : this(source.Close, 0, false) { } - public WMAPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public WMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public WMAPE_Series(TSeries source) : this(source, 0, false) { } - public WMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - - double _sma = _buffer.Average(); - - double _div = 0; - double _wmape = 0; - for (int i = 0; i < _buffer.Count; i++) - { - _wmape += Math.Abs(_buffer[i] - _sma); - _div += Math.Abs(_buffer[i]); - } - _wmape = (_div != 0) ? _wmape / _div : double.PositiveInfinity; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wmape); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/WMA_Series.cs b/archive/Calculations/_Updated/WMA_Series.cs deleted file mode 100644 index 7fbf16a1..00000000 --- a/archive/Calculations/_Updated/WMA_Series.cs +++ /dev/null @@ -1,117 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -/* -WMA: (linearly) Weighted Moving Average - The weights are linearly decreasing over the period and the most recent data has - the heaviest weight. - -Sources: - https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/weighted-moving-average-wma/ - https://www.technicalindicators.net/indicators-technical-analysis/83-moving-averages-simple-exponential-weighted - - */ - -public class WMA_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - private System.Collections.Generic.List _weights; - protected int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected int _len; - public int Len - { - get { return _len; } - set { _len = value; } - } - - //core constructors - public WMA_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"WMA({period})"; - _len = 1; - _weights = CalculateWeights(_period); - } - public WMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public WMA_Series() : this(period: 0, useNaN: false) { } - public WMA_Series(int period) : this(period: period, useNaN: false) { } - public WMA_Series(TBars source) : this(source.Close, 0, false) { } - public WMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public WMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public WMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - if (_period == 0) - { - _weights = CalculateWeights(_len); - _len++; - } - double _wma = 0; - double totalWeights = (_buffer.Count * (_buffer.Count + 1)) * 0.5; - object lockObj = new object(); - Parallel.For(0, _buffer.Count, i => - { - double temp = _buffer[i] * this._weights[i]; - lock (lockObj) { _wma += temp; } - }); - _wma /= totalWeights; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //calculating weights - private static List CalculateWeights(int period) - { - List weights = new List(period); - for (int i = 0; i < period; i++) - { - weights.Add(i + 1); - } - return weights; - } - - //reset calculation - public override void Reset() - { - _len = 0; - _weights = CalculateWeights(_period); - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ZLEMA_Series.cs b/archive/Calculations/_Updated/ZLEMA_Series.cs deleted file mode 100644 index 676e7fe5..00000000 --- a/archive/Calculations/_Updated/ZLEMA_Series.cs +++ /dev/null @@ -1,105 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -ZLEMA: Zero Lag Exponential Moving Average - The Zero lag exponential moving average (ZLEMA) indicator was created by John - Ehlers and Ric Way. - -The formula for a given N-Day period and for a given Data series is: - Lag = (Period-1)/2 - Ema Data = {Data+(Data-Data(Lag days ago)) - ZLEMA = EMA (EmaData,Period) - -Remark: - The idea is do a regular exponential moving average (EMA) calculation but on a - de-lagged data instead of doing it on the regular data. Data is de-lagged by - removing the data from "lag" days ago thus removing (or attempting to remove) - the cumulative lag effect of the moving average. - - */ - -public class ZLEMA_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly EMA_Series _ema; - - //core constructor - public ZLEMA_Series(int period, bool useNaN, bool useSMA) - { - _period = period; - _NaN = useNaN; - Name = $"ZLEMA({period})"; - _len = 1; - _ema = new(period); - } - //generic constructors (source) - - public ZLEMA_Series() : this(0, false, true) { } - public ZLEMA_Series(int period) : this(period, false, true) { } - public ZLEMA_Series(TBars source) : this(source.Close, 0, false) { } - public ZLEMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public ZLEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public ZLEMA_Series(TSeries source, int period) : this(source, period, false, true) { } - public ZLEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - public ZLEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - int _lag; - if (_period == 0) - { - _lag = (int)((_len - 1) * 0.5); - _len++; - } - else { _lag = (int)((_period - 1) * 0.5); } - _lag = Math.Min(_lag, _buffer.Count - 1); - _lag = Math.Max(_lag, 0) + 1; - double _zlValue = 2 * TValue.v - _buffer[^_lag]; - double _zlema = _ema.Add((TValue.t, _zlValue), update).v; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlema); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - _ema.Reset(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ZL_Series.cs b/archive/Calculations/_Updated/ZL_Series.cs deleted file mode 100644 index ee7aa4ce..00000000 --- a/archive/Calculations/_Updated/ZL_Series.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -ZL: Zero Lag - Data is de-lagged by removing the data from “lag” days ago, thus removing - (or attempting to) the cumulative effect of the moving average. - -Calculation: - Lag = (Period-1)/2 - ZL = Data + (Data - Data(Lag days ago) ) - -Sources: - https://mudrex.com/blog/zero-lag-ema-trading-strategy/ - - */ - -public class ZL_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly EMA_Series _ema; - - //core constructor - public ZL_Series(int period, bool useNaN, bool useSMA) - { - _period = period; - _NaN = useNaN; - Name = $"ZL({period})"; - _len = 1; - _ema = new(period); - } - //generic constructors (source) - - public ZL_Series() : this(0, false, true) { } - public ZL_Series(int period) : this(period, false, true) { } - public ZL_Series(TBars source) : this(source.Close, 0, false) { } - public ZL_Series(TBars source, int period) : this(source.Close, period, false) { } - public ZL_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public ZL_Series(TSeries source, int period) : this(source, period, false, true) { } - public ZL_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - public ZL_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - int _lag; - if (_period == 0) - { - _lag = (int)((_len - 1) * 0.5); - _len++; - } - else { _lag = (int)((_period - 1) * 0.5); } - _lag = Math.Min(_lag, _buffer.Count - 1); - _lag = Math.Max(_lag, 0) + 1; - double _zlValue = 2 * TValue.v - _buffer[^_lag]; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlValue); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - _ema.Reset(); - } -} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ZSCORE_Series.cs b/archive/Calculations/_Updated/ZSCORE_Series.cs deleted file mode 100644 index 52c45d70..00000000 --- a/archive/Calculations/_Updated/ZSCORE_Series.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ZSCORE: number of standard deviations from SMA - Z-score describes a value's relationship to the mean of a series, as measured in - terms of standard deviations from the mean. If a Z-score is 0, it indicates that - the data point's score is identical to the mean score. A Z-score of 1.0 would - indicate a value that is one standard deviation from the mean. Z-scores may be - positive or negative, with a positive value indicating the score is above the - mean and a negative score indicating it is below the mean. - -Sources: - https://en.wikipedia.org/wiki/Z-score - https://www.investopedia.com/terms/z/zscore.asp - -Calculation: - std = std * STDEV(close, length) - mean = SMA(close, length) - ZSCORE = (close - mean) / std - - */ - -public class ZSCORE_Series : TSeries -{ - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public ZSCORE_Series(int period, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"ZSCORE({period})"; - } - public ZSCORE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public ZSCORE_Series() : this(period: 0, useNaN: false) { } - public ZSCORE_Series(int period) : this(period: period, useNaN: false) { } - public ZSCORE_Series(TBars source) : this(source.Close, 0, false) { } - public ZSCORE_Series(TBars source, int period) : this(source.Close, period, false) { } - public ZSCORE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public ZSCORE_Series(TSeries source) : this(source, 0, false) { } - public ZSCORE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - double _sma = _buffer.Average(); - - double _pvar = 0; - for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _pvar /= this._buffer.Count; - double _psdev = Math.Sqrt(_pvar); - double _zscore = (_psdev == 0) ? 1 : (TValue.v - _sma) / _psdev; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zscore); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/archive/Indicators/Charts/2MACross_chart.cs b/archive/Indicators/Charts/2MACross_chart.cs deleted file mode 100644 index d655896b..00000000 --- a/archive/Indicators/Charts/2MACross_chart.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class MovingAverage_chart : Indicator -{ - #region Parameters - [InputParameter("MA1: Type:", 0, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA1type = 15; - - [InputParameter("MA1: Smoothing period:", 1, 1, 999, 1, 1)] - private int MA1Period = 10; - - [InputParameter("MA1: Data source:", 2, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA1DataSource = 3; - - [InputParameter("MA2: Type:", 3, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA2type = 16; - - [InputParameter("MA2: Smoothing period:", 4, 1, 999, 1, 1)] - private int MA2Period = 50; - - [InputParameter("MA2: Data source:", 5, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA2DataSource = 8; - - [InputParameter("Long trades", 6)] - private bool LongTrades = true; - - [InputParameter("Short trades", 6)] - private bool ShortTrades = true; - - #endregion Parameters - - protected HistoricalData History; - private TBars bars; - - /////// - private TSeries MA1, MA2; - private CROSS_Series trades; - private COMPARE_Series overunder; - - /////// - - public MovingAverage_chart() - { - this.SeparateWindow = false; - this.Name = "MAs Crossover"; - this.AddLineSeries("MA1", Color.LimeGreen, 2, LineStyle.Solid); - this.AddLineSeries("MA2", Color.OrangeRed, 2, LineStyle.Solid); - } - - protected override void OnInit() - { - this.bars = new(); - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - for (int i = this.History.Count - 1; i >= 0; i--) - { - var rec = this.History[i, SeekOriginHistory.Begin]; - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - this.Name = "MAs Cross: [ "; - switch (MA1type) - { - case 0: - MA1 = new SMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA1 = new EMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA1 = new WMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA1 = new T3_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA1 = new SMMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA1 = new TRIMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA1 = new DWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period); - this.Name += $"FWMA"; - break; - case 8: - MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA1 = new TEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA1 = new ALMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA1 = new HMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA1 = new HEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA1Period); - MA1 = new MAMA_Series(source: bars.Select(this.MA1DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA1 = new KAMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA1 = new ZLEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA1 = new JMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"JMA"; - break; - } - - this.Name = this.Name + $" ({MA1Period}:{TBars.SelectStr(this.MA1DataSource)}) : "; - - switch (MA2type) - { - case 0: - MA2 = new SMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA2 = new EMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA2 = new WMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA2 = new T3_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA2 = new SMMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA2 = new TRIMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA2 = new DWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period); - this.Name += $"FWMA"; - break; - case 8: - MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA2 = new TEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA2 = new ALMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA2 = new HMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA2 = new HEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA2Period); - MA2 = new MAMA_Series(source: bars.Select(this.MA2DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA2 = new KAMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA2 = new ZLEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA2 = new JMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"JMA"; - break; - } - this.Name += $"({MA2Period}:{TBars.SelectStr(this.MA2DataSource)}) ]"; - - int maxKeep = Math.Max(Math.Max(this.MA1Period, this.MA2Period), 100); - MA1.Keep = maxKeep; - MA2.Keep = maxKeep; - trades.Keep = maxKeep; - overunder.Keep = maxKeep; - - overunder = new(MA1, MA2); - trades = new(MA1, MA2); - } - - protected override void OnUpdate(UpdateArgs args) - { - bool update = !(args.Reason == UpdateReason.NewBar || - args.Reason == UpdateReason.HistoricalBar); - this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), - this.GetPrice(PriceType.High), - this.GetPrice(PriceType.Low), - this.GetPrice(PriceType.Close), - this.GetPrice(PriceType.Volume), update); - this.SetValue(this.MA1[^1].v, lineIndex: 0); - this.SetValue(this.MA2[^1].v, lineIndex: 1); - - if (trades[^1].v == 1) - { - this.EndCloud(0, 1, Color.Empty); - if (LongTrades) - { - this.LinesSeries[0].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow)); - this.BeginCloud(0, 1, Color.FromArgb(127, Color.Green)); - } - if (ShortTrades) - { - this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow)); - } - } - if (trades[^1].v == -1) - { - this.EndCloud(0, 1, Color.Empty); - if (ShortTrades) - { - this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow)); - this.BeginCloud(0, 1, Color.FromArgb(127, Color.Red)); - } - if (LongTrades) - { - this.LinesSeries[0].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); - } - } - } - public override void OnPaintChart(PaintChartEventArgs args) - { - base.OnPaintChart(args); - if (this.CurrentChart == null) { return; } - Graphics graphics = args.Graphics; - var mainWindow = this.CurrentChart.MainWindow; - int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); - int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); - int historycount = HistoricalData.Count; - int ymax = mainWindow.ClientRectangle.Height; - int xmax = mainWindow.ClientRectangle.Width; - - /* - for (int i = leftIndex; i <= rightIndex; i++) { - int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); - int width = this.CurrentChart.BarsWidth; - int height = (int)((equity[i+historycount].v) *proportion); - - Brush bb = Brushes.DarkSlateGray; - bb = (overunder[i+historycount].v>0 && LongTrades)? Brushes.Green : bb; - bb = (overunder[i + historycount].v < 0 && ShortTrades) ? Brushes.Red : bb; - - graphics.FillRectangle(bb, xi, ymax - height, width, height); - } - */ - } -} diff --git a/archive/Indicators/Charts/2MASlope_chart.cs b/archive/Indicators/Charts/2MASlope_chart.cs deleted file mode 100644 index db75d157..00000000 --- a/archive/Indicators/Charts/2MASlope_chart.cs +++ /dev/null @@ -1,320 +0,0 @@ -using System; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class MovingAverageSlope_chart : Indicator -{ - #region Parameters - [InputParameter("MA1: Type:", 0, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA1type = 16; - - [InputParameter("MA1: Smoothing period:", 1, 1, 999, 1, 1)] - private int MA1Period = 10; - - [InputParameter("MA1: Data source:", 2, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA1DataSource = 3; - - [InputParameter("MA2: Type:", 3, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA2type = 6; - - [InputParameter("MA2: Smoothing period:", 4, 1, 999, 1, 1)] - private int MA2Period = 50; - - [InputParameter("MA2: Data source:", 5, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA2DataSource = 8; - - [InputParameter("Data required for slope calc:", 6, 2, 10, 1, 1)] - private int SlopePeriod = 3; - - [InputParameter("Long trades", 7)] - private bool LongTrades = true; - - [InputParameter("Short trades", 8)] - private bool ShortTrades; - - #endregion Parameters - - protected HistoricalData History; - private TBars bars; - - /////// - private TSeries MA1, MA2; - private SLOPE_Series sMA1, sMA2; - private CROSS_Series sig1, sig2; - - private bool inLong, inShort; - /////// - - public MovingAverageSlope_chart() - { - this.SeparateWindow = false; - this.Name = "Slopes convergence"; - this.AddLineSeries("MA1", Color.DarkSlateGray, 2, LineStyle.Solid); - this.AddLineSeries("MA2", Color.DarkSlateGray, 2, LineStyle.Solid); - } - - protected override void OnInit() - { - this.bars = new(); - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - for (int i = this.History.Count - 1; i >= 0; i--) - { - var rec = this.History[i, SeekOriginHistory.Begin]; - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - this.Name = "Slopes convergence: [ "; - switch (MA1type) - { - case 0: - MA1 = new SMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA1 = new EMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA1 = new WMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA1 = new T3_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA1 = new SMMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA1 = new TRIMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA1 = new DWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period); - this.Name += $"FWMA"; - break; - case 8: - MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA1 = new TEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA1 = new ALMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA1 = new HMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA1 = new HEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA1Period); - MA1 = new MAMA_Series(source: bars.Select(this.MA1DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA1 = new KAMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA1 = new ZLEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA1 = new JMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"JMA"; - break; - } - - this.Name = this.Name + $" ({MA1Period}:{TBars.SelectStr(this.MA1DataSource)}) : "; - - switch (MA2type) - { - case 0: - MA2 = new SMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA2 = new EMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA2 = new WMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA2 = new T3_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA2 = new SMMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA2 = new TRIMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA2 = new DWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period); - this.Name += $"FWMA"; - break; - case 8: - MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA2 = new TEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA2 = new ALMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA2 = new HMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA2 = new HEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA2Period); - MA2 = new MAMA_Series(source: bars.Select(this.MA2DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA2 = new KAMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA2 = new ZLEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA2 = new JMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"JMA"; - break; - } - this.Name += $"({MA2Period}:{TBars.SelectStr(this.MA2DataSource)}) ]"; - - sMA1 = new(MA1, SlopePeriod); - sMA2 = new(MA2, SlopePeriod); - sig1 = new(sMA1, 0); - sig2 = new(sMA2, 0); - - int maxKeep = Math.Max(Math.Max(this.MA1Period, this.MA2Period), 100); - - MA1.Keep = maxKeep; - MA2.Keep = maxKeep; - sMA1.Keep = maxKeep; - sMA2.Keep = maxKeep; - sig1.Keep = maxKeep; - sig2.Keep = maxKeep; - } - - protected override void OnUpdate(UpdateArgs args) - { - bool update = !(args.Reason == UpdateReason.NewBar || - args.Reason == UpdateReason.HistoricalBar); - this.bars.Add(this.Time(), this.Open(), this.High(), this.Low(), this.Close(), this.Volume(), update); - this.SetValue(this.MA1[^1].v, lineIndex: 0); - this.SetValue(this.MA2[^1].v, lineIndex: 1); - - Color s1Color = (this.sMA1[^1].v > 0) ? Color.LimeGreen : Color.OrangeRed; - Color s2Color = (this.sMA2[^1].v > 0) ? Color.LimeGreen : Color.OrangeRed; - - this.LinesSeries[0].SetMarker(0, s1Color); - this.LinesSeries[1].SetMarker(0, s2Color); - - if (sig1[^1].v > 0 || sig2[^1].v > 0) - { - if (sMA1[^1].v >= 0 && sMA2[^1].v >= 0 && LongTrades) - { - inLong = true; - this.BeginCloud(0, 1, Color.FromArgb(127, Color.DarkGreen)); - this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 0 : 1].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow)); - } - else - { - this.EndCloud(0, 1, Color.Empty); - if (inShort && this.Count > 1) - { - this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow)); - inShort = false; - } - } - } - - if (sig1[^1].v < 0 || sig2[^1].v < 0) - { - if (sMA1[^1].v <= 0 && sMA2[^1].v <= 0 && ShortTrades) - { - inShort = true; - this.BeginCloud(0, 1, Color.FromArgb(100, Color.Red)); - this.LinesSeries[(this.MA1[^1].v > this.MA2[^1].v) ? 0 : 1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow)); - } - else - { - this.EndCloud(0, 1, Color.Empty); - if (inLong && this.Count > 1) - { - LinesSeries[(this.MA1[^1].v > this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); - inLong = false; - } - } - } - } - public override void OnPaintChart(PaintChartEventArgs args) - { - base.OnPaintChart(args); - if (this.CurrentChart == null) { return; } - Graphics graphics = args.Graphics; - var mainWindow = this.CurrentChart.MainWindow; - int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); - int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); - /* - int historycount = HistoricalData.Count; - int ymax = mainWindow.ClientRectangle.Height; - - - for (int i = leftIndex; i <= rightIndex; i++) { - int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); - int width = this.CurrentChart.BarsWidth; - int height = (int)((equity[i+historycount].v) *proportion); - - Brush bb = Brushes.DarkSlateGray; - bb = (overunder[i+historycount].v>0 && LongTrades)? Brushes.Green : bb; - bb = (overunder[i + historycount].v < 0 && ShortTrades) ? Brushes.Red : bb; - - graphics.FillRectangle(bb, xi, ymax - height, width, height); - } - */ - } -} diff --git a/archive/Indicators/Charts/JMA_chart.cs b/archive/Indicators/Charts/JMA_chart.cs deleted file mode 100644 index 1f7ec9aa..00000000 --- a/archive/Indicators/Charts/JMA_chart.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Diagnostics; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -using TradingPlatform.BusinessLayer.Chart; -namespace QuanTAlib; - -public class JMA_chart : Indicator -{ - #region Parameters - - [InputParameter("Data source", 0, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int DataSource = 3; - - [InputParameter("Smoothing period", 1, 1, 999, 1, 1)] - private int Period = 9; - - [InputParameter("Volatility short", 2, 3, 50, 1, 1)] - private int Vshort = 10; - - [InputParameter("Volatility long", 3, 20, 500, 1, 1)] - private int Vlong = 65; - - [InputParameter("Phase", 4, -100, 100, 1, 2)] - private double Jphase; - - #endregion Parameters - - /////// - private JMA_Series indicator; - /////// - - protected TBars bars; - protected IChartWindow mainWindow; - protected Graphics graphics; - protected int firstOnScreenBarIndex, lastOnScreenBarIndex; - protected HistoricalData History; - protected int HistPeriod; - public JMA_chart() - { - Name = "JMA - Jurik Moving Avg"; - Description = "Jurik Moving Average description"; - AddLineSeries(lineName: "JMA", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); - SeparateWindow = false; - HistPeriod = Period; - } - - - protected override void OnInit() - { - base.OnInit(); - bars = new(); - var dur1 = this.HistoricalData.FromTime; - var dur = this.HistoricalData.Period.Duration.TotalSeconds * (HistPeriod * 4); //seconds of two periods - - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - - for (int i = this.History.Count - 1; i >= 0; i--) - { - - var rec = this.History[i, SeekOriginHistory.Begin]; - - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - - indicator = new(source: bars.Select(DataSource), period: Period, phase: Jphase, vshort: Vshort, vlong: Vlong, useNaN: true); - indicator.Keep = Math.Max(Period, 100); - } - - protected override void OnUpdate(UpdateArgs args) - { - base.OnUpdate(args); - bars.Add(Time(), GetPrice(PriceType.Open), - GetPrice(PriceType.High), - GetPrice(PriceType.Low), - GetPrice(PriceType.Close), - GetPrice(PriceType.Volume), - update: !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar)); - - this.SetValue(indicator[^1].v, lineIndex: 0); - } - public override void OnPaintChart(PaintChartEventArgs args) - { - base.OnPaintChart(args); - if (this.CurrentChart == null) - { - return; - } - - graphics = args.Graphics; - mainWindow = this.CurrentChart.MainWindow; - - DateTime leftTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left); - DateTime rightTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right); - firstOnScreenBarIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(leftTime); - lastOnScreenBarIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(rightTime)); - } - -} diff --git a/archive/Indicators/Charts/TrailingStop.cs b/archive/Indicators/Charts/TrailingStop.cs deleted file mode 100644 index ad480826..00000000 --- a/archive/Indicators/Charts/TrailingStop.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Diagnostics; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class TrailingStop_chart : Indicator -{ - #region Parameters - - [InputParameter("Period", 0, 1, 100, 1, 1)] - protected int _period = 30; - - [InputParameter("Factor", 1, 1, 100, 0.1, 1)] - protected double _factor = 10; - - [InputParameter("Long TS", 2)] - private bool _LongTS = true; - - [InputParameter("Short TS", 3)] - private bool _ShortTS = true; - - #endregion Parameters - - /////// - private HistoricalData History; - private TBars bars; - private ATR_Series _atr; - private double _tslineL, _ratchetL, _tslineS, _ratchetS; - - /////// - - public TrailingStop_chart() - { - Name = $"ATR Trailing Stop"; - AddLineSeries(lineName: "TrailingATR Long", lineColor: Color.Yellow, lineWidth: 1, lineStyle: LineStyle.Dot); - AddLineSeries(lineName: "Ratchet Long", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); - - AddLineSeries(lineName: "TrailingATR Short", lineColor: Color.Yellow, lineWidth: 1, lineStyle: LineStyle.Dot); - AddLineSeries(lineName: "Ratchet Short", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); - - SeparateWindow = false; - } - - - protected override void OnInit() - { - this.Name = $"Trailing Stop (ATR:{_period}, Mult:{_factor:f2})"; - this.bars = new(); - - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - for (int i = this.History.Count - 1; i >= 0; i--) - { - var rec = this.History[i, SeekOriginHistory.Begin]; - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - _atr = new(source: bars, _period, useNaN: true); - _ratchetL = Double.NegativeInfinity; - _ratchetS = Double.PositiveInfinity; - - this.LinesSeries[0].Visible = _LongTS; - this.LinesSeries[1].Visible = _LongTS; - this.LinesSeries[2].Visible = _ShortTS; - this.LinesSeries[3].Visible = _ShortTS; - } - - protected override void OnUpdate(UpdateArgs args) - { - bool update = !(args.Reason == UpdateReason.NewBar || - args.Reason == UpdateReason.HistoricalBar); - this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), - this.GetPrice(PriceType.High), - this.GetPrice(PriceType.Low), - this.GetPrice(PriceType.Close), - this.GetPrice(PriceType.Volume), update); - - _tslineL = bars.High[^1].v - (_factor * _atr[^1].v); - _ratchetL = Math.Max(_tslineL, _ratchetL); - if (_ratchetL > bars.Low[^1].v) - { - this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.Yellow, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); - _ratchetL = _tslineL; - } - - _tslineS = bars.High[^1].v + (_factor * _atr[^1].v); - _ratchetS = Math.Min(_tslineS, _ratchetS); - if (_ratchetS < bars.High[^1].v) - { - this.LinesSeries[3].SetMarker(0, new IndicatorLineMarker(Color.Yellow, upperIcon: IndicatorLineMarkerIconType.UpArrow)); - _ratchetS = _tslineS; - } - - this.SetValue(_tslineL, lineIndex: 0); - this.SetValue(_ratchetL, lineIndex: 1); - this.SetValue(_tslineS, lineIndex: 2); - this.SetValue(_ratchetS, lineIndex: 3); - } -} - diff --git a/archive/Indicators/Indicators.csproj b/archive/Indicators/Indicators.csproj deleted file mode 100644 index 5b9da593..00000000 --- a/archive/Indicators/Indicators.csproj +++ /dev/null @@ -1,56 +0,0 @@ - - - net7.0 - preview - false - AnyCPU - Indicator - QuanTAlib_Indicators - QuanTAlib - embedded - AnyCPU - disable - False - ..\.sonarlint\mihakralj_quantalibcsharp.ruleset - 0.2.1.0 - 0.2.1.0 - 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d - 0.2.1-dev.2 - NETSDK1057 - true - NETSDK1057 - - - True - 3 - True - anycpu - full - - - embedded - True - 3 - True - anycpu - - - - - - - - - - - - - QuanTAlib\%(RecursiveDir)%(Filename)%(Extension) - - - - - ..\.github\TradingPlatform.BusinessLayer.dll - - - \ No newline at end of file diff --git a/archive/QuanTAlib.sln_old b/archive/QuanTAlib.sln_old deleted file mode 100644 index a2d92cf3..00000000 --- a/archive/QuanTAlib.sln_old +++ /dev/null @@ -1,24 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32210.308 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calculations", "v2\calculations.csproj", "{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {E5592DC2-0542-45B2-A0CF-C6B1EDC72B87} - EndGlobalSection -EndGlobal \ No newline at end of file diff --git a/archive/Strategies/Strategies.csproj b/archive/Strategies/Strategies.csproj deleted file mode 100644 index e665dc50..00000000 --- a/archive/Strategies/Strategies.csproj +++ /dev/null @@ -1,53 +0,0 @@ - - - net7.0 - preview - false - AnyCPU - Strategy - QuanTAlib_Strategies - QuanTAlib - embedded - AnyCPU - disable - False - ..\.sonarlint\mihakralj_quantalibcsharp.ruleset - 0.2.1.0 - 0.2.1.0 - 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d - 0.2.1-dev.2 - NETSDK1057 - true - NETSDK1057 - - - True - 3 - True - anycpu - full - - - embedded - True - 3 - True - anycpu - - - - - - - QuanTAlib\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - - ..\.github\TradingPlatform.BusinessLayer.dll - - - \ No newline at end of file diff --git a/archive/Tests/Basic tests/Indicators.cs b/archive/Tests/Basic tests/Indicators.cs deleted file mode 100644 index 431aa94f..00000000 --- a/archive/Tests/Basic tests/Indicators.cs +++ /dev/null @@ -1,156 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Basics; -#nullable disable -public class Indicators -{ - private static Type[] maSeriesTypes = new Type[] - { - typeof(SMA_Series), - typeof(EMA_Series), - typeof(DEMA_Series), - typeof(TEMA_Series), - typeof(WMA_Series), - typeof(ALMA_Series), - typeof(DWMA_Series), - typeof(FWMA_Series), - typeof(HMA_Series), - typeof(ZLEMA_Series), - typeof(RMA_Series), - typeof(HEMA_Series), - typeof(JMA_Series), - typeof(CUSUM_Series), - typeof(SMMA_Series), - typeof(T3_Series), - typeof(KAMA_Series), - typeof(TRIMA_Series), - typeof(MAMA_Series), - typeof(HWMA_Series), - }; - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Name_exists(Type classType) - { - TSeries data = new("Data") { 1, 2, 3 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.NotEmpty(MA_Series.Name); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Series_Length(Type classType) - { - GBM_Feed feed = new(1000); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.Equal(1000, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Return_data(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var result = MA_Series.Add(20); - Assert.Equal(result.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Update(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var pre_update = MA_Series.Last.v; - - double pre_data = data.Last.v; - data.Add(20, true); - data.Add(pre_data, true); - - Assert.Equal(pre_update, MA_Series.Last.v); - Assert.Equal(data.Count, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_zero(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries; - Assert.Equal(data.Count, MA_Series.Count); - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Reset(Type classType) - { - GBM_Feed feed = new(10); - TSeries data = feed.OHLC4; - var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries; - MA_Series.Reset(); - data.Add(0); - Assert.Equal(data.Last.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_one(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries; - Assert.InRange(MA_Series.Last.v - data.Last.v, -10e-6, 10e-6); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void NaN_test(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.True(double.IsNaN(MA_Series[0].v)); - Assert.True(double.IsNaN(MA_Series[8].v)); - Assert.False(double.IsNaN(MA_Series[9].v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Edge_numbers(Type classType) - { - TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity }; - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.Equal(4, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void handling_NaN(Type classType) - { - TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - public static IEnumerable MASeriesData() - { - foreach (var type in maSeriesTypes) - { - yield return new object[] { type }; - } - } -} -#nullable restore \ No newline at end of file diff --git a/archive/Tests/Basic tests/Oscillators.cs b/archive/Tests/Basic tests/Oscillators.cs deleted file mode 100644 index fcf86f06..00000000 --- a/archive/Tests/Basic tests/Oscillators.cs +++ /dev/null @@ -1,161 +0,0 @@ -using Xunit; -using System; -using System.Runtime.InteropServices; -using QuanTAlib; - -namespace Basics; -#nullable disable -public class Oscillators -{ - private static Type[] maSeriesTypes = new[] - { - typeof(BIAS_Series), - typeof(MAX_Series), - typeof(MIN_Series), - typeof(MIDPOINT_Series), - typeof(ZL_Series), - typeof(DECAY_Series), - typeof(ENTROPY_Series), - typeof(KURTOSIS_Series), - typeof(MAD_Series), - typeof(MAPE_Series), - typeof(MAE_Series), - typeof(MSE_Series), - typeof(SDEV_Series), - typeof(SMAPE_Series), - typeof(WMAPE_Series), - typeof(SSDEV_Series), - typeof(VAR_Series), - typeof(SVAR_Series), - typeof(MEDIAN_Series), - typeof(ZSCORE_Series), - typeof(CMO_Series), - typeof(RSI_Series), - typeof(TRIX_Series), - typeof(BBANDS_Series), -}; - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Name_exists(Type classType) - { - TSeries data = new("Data") { 1, 2, 3 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.NotEmpty(MA_Series.Name); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Series_Length(Type classType) - { - GBM_Feed feed = new(1000); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.Equal(1000, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Return_data(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var result = MA_Series.Add(20); - Assert.Equal(result.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Update(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var pre_update = MA_Series.Last.v; - - double pre_data = data.Last.v; - data.Add(20, true); - data.Add(pre_data, true); - - Assert.Equal(pre_update, MA_Series.Last.v); - Assert.Equal(data.Count, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_zero(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries; - Assert.Equal(data.Count, MA_Series.Count); - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Reset(Type classType) - { - GBM_Feed feed = new(10); - TSeries data = feed.OHLC4; - var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries; - MA_Series.Reset(); - data.Add(1); - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_one(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries; - Assert.False(double.IsNaN(MA_Series[^1].v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void NaN_test(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.True(double.IsNaN(MA_Series[0].v)); - Assert.True(double.IsNaN(MA_Series[8].v)); - Assert.False(double.IsNaN(MA_Series[9].v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Edge_numbers(Type classType) - { - TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity }; - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.Equal(4, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void handling_NaN(Type classType) - { - TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - public static IEnumerable MASeriesData() - { - foreach (var type in maSeriesTypes) - { - yield return new object[] { type }; - } - } -} -#nullable restore \ No newline at end of file diff --git a/archive/Tests/Basic tests/TBars_input.cs b/archive/Tests/Basic tests/TBars_input.cs deleted file mode 100644 index 02489ff7..00000000 --- a/archive/Tests/Basic tests/TBars_input.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Xunit; -using System; -using System.Runtime.InteropServices; -using QuanTAlib; - -namespace Basics; -#nullable disable -public class TBars -{ - private static Type[] maSeriesTypes = new Type[] - { - typeof(ATR_Series), - typeof(ATRP_Series), - typeof(TR_Series), - typeof(ADL_Series), - typeof(CCI_Series), - typeof(OBV_Series), - typeof(ADOSC_Series), - typeof(MIDPRICE_Series), - }; - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Name_exists(Type classType) - { - GBM_Feed data = new(10); - - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - Assert.NotEmpty(MA_Series.Name); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Series_Length(Type classType) - { - GBM_Feed data = new(1000); - - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - Assert.Equal(1000, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Return_data(Type classType) - { - GBM_Feed data = new(10); - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - var result = MA_Series.Add((DateTime.Today, 1, 2, 3, 4, 5)); - Assert.Equal(result.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Update(Type classType) - { - GBM_Feed data = new(10); - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - var pre_update = MA_Series.Last; - - var pre_data = data.Last; - data.Add((DateTime.Today, 1, 2, 3, 4, 5), true); - data.Add(pre_data, true); - - Assert.Equal(pre_update.v, MA_Series.Last.v); - Assert.Equal(data.Count, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Reset(Type classType) - { - GBM_Feed data = new(10); - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - MA_Series.Reset(); - data.Add(); - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_default(Type classType) - { - GBM_Feed data = new(100); - - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - public static IEnumerable MASeriesData() - { - foreach (var type in maSeriesTypes) - { - yield return new object[] { type }; - } - } -} -#nullable restore \ No newline at end of file diff --git a/archive/Tests/Pairs/ADD_Test.cs b/archive/Tests/Pairs/ADD_Test.cs deleted file mode 100644 index 2c86e5a8..00000000 --- a/archive/Tests/Pairs/ADD_Test.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class ADD_Test -{ - [Fact] - public void ADDSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - ADD_Series c = new(a, b); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void ADDSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - ADD_Series c = new(a, 10.0); - Assert.Equal(15, c.Last().v); - } - - [Fact] - public void ADDDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - ADD_Series c = new(10.0, a); - Assert.Equal(15, c.Last().v); - } - - [Fact] - public void ADDEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - ADD_Series c = new(a, b); - a.Add(2); - b.Add(2); - Assert.Equal(4, c.Last().v); - } - - [Fact] - public void ADDUpdateDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - double b = 10; - ADD_Series c = new(a, b); - a.Add(0, true); - Assert.Equal(10, c.Last().v); - } - - [Fact] - public void ADDUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - ADD_Series c = new(a, b); - a.Add(10, true); - b.Add(10, true); - Assert.Equal(20, c.Last().v); - } -} diff --git a/archive/Tests/Pairs/DIV_Test.cs b/archive/Tests/Pairs/DIV_Test.cs deleted file mode 100644 index 0b011bc6..00000000 --- a/archive/Tests/Pairs/DIV_Test.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class DIV_Test -{ - [Fact] - public void DIVSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15 }; - TSeries b = new() { 5, 4, 3, 2, 1, 3 }; - DIV_Series c = new(a, b); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void DIVSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; - DIV_Series c = new(a, 0); - Assert.Equal(double.PositiveInfinity, c.Last().v); - } - - [Fact] - public void DIVDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 3.0 }; - DIV_Series c = new(12.0, a); - Assert.Equal(4.0, c.Last().v); - } - - [Fact] - public void DIVEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - DIV_Series c = new(a, b); - a.Add(12.0); - b.Add(2); - Assert.Equal(6.0, c.Last().v); - } - - [Fact] - public void DIVUpdatewDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15 }; - double b = 2; - DIV_Series c = new(a, b); - a.Add(10, true); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void DIVUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - DIV_Series c = new(a, b); - a.Add(10, true); - b.Add(2, true); - Assert.Equal(5, c.Last().v); - } -} diff --git a/archive/Tests/Pairs/MUL_Test.cs b/archive/Tests/Pairs/MUL_Test.cs deleted file mode 100644 index cc1c9718..00000000 --- a/archive/Tests/Pairs/MUL_Test.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class MUL_Test -{ - [Fact] - public void MULSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - MUL_Series c = new(a, b); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void MULSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - MUL_Series c = new(a, 10.0); - Assert.Equal(50, c.Last().v); - } - - [Fact] - public void MULDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - MUL_Series c = new(5.0, a); - Assert.Equal(25, c.Last().v); - } - - [Fact] - public void MULEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - MUL_Series c = new(a, b); - a.Add(2); - b.Add(5); - Assert.Equal(10, c.Last().v); - } - - [Fact] - public void MULUpdateDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - double b = 10; - MUL_Series c = new(a, b); - a.Add(2, true); - Assert.Equal(20, c.Last().v); - } - - [Fact] - public void MULUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - MUL_Series c = new(a, b); - a.Add(10, true); - b.Add(10, true); - Assert.Equal(100, c.Last().v); - } -} diff --git a/archive/Tests/Pairs/SUB_Test.cs b/archive/Tests/Pairs/SUB_Test.cs deleted file mode 100644 index 2a96d81d..00000000 --- a/archive/Tests/Pairs/SUB_Test.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class SUB_Test -{ - [Fact] - public void SUBSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - SUB_Series c = new(a, b); - Assert.Equal(4, c.Last().v); - } - - [Fact] - public void SUBSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; - SUB_Series c = new(a, 10.0); - Assert.Equal(5.0, c.Last().v); - } - - [Fact] - public void SUBDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; - SUB_Series c = new(10.0, a); - Assert.Equal(-5.0, c.Last().v); - } - - [Fact] - public void SUBEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - SUB_Series c = new(a, b); - a.Add(7.0); - b.Add(2); - Assert.Equal(5.0, c.Last().v); - } - - [Fact] - public void SUBUpdatewDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15 }; - double b = 10; - SUB_Series c = new(a, b); - a.Add(1, true); - Assert.Equal(-9, c.Last().v); - } - - [Fact] - public void SUBUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - SUB_Series c = new(a, b); - a.Add(10, true); - b.Add(0, true); - Assert.Equal(10, c.Last().v); - } -} diff --git a/archive/Tests/Pairs/TBars_Test.cs b/archive/Tests/Pairs/TBars_Test.cs deleted file mode 100644 index cae4ecec..00000000 --- a/archive/Tests/Pairs/TBars_Test.cs +++ /dev/null @@ -1,112 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Bars; -public class TBars_Test -{ - [Fact] - public void InsertingTuple() - { - TBars s = new() { (t: DateTime.Today, o: double.Epsilon, h: double.NaN, l: Double.MaxValue, c: Double.NegativeInfinity, v: Double.PositiveInfinity) }; - var tup = (t: DateTime.Today, o: double.Epsilon, h: double.NaN, l: Double.MaxValue, - c: Double.NegativeInfinity, v: Double.PositiveInfinity); - Assert.Equal(tup, s[^1]); - } - - [Fact] - public void Casting_Parameters() - { - TBars s = new() - { - { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1, false } - }; - Assert.Equal(0.1, s[^1].o); - Assert.Equal(1.1, s[^1].h); - Assert.Equal(2.1, s[^1].l); - Assert.Equal(3.1, s[^1].c); - Assert.Equal(4.1, s[^1].v); - Assert.Equal(DateTime.Today, s[^1].t); - Assert.Single(s); - } - - [Fact] - public void Updating_Value() - { - TBars s = new() - { - { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1 } - }; - s.Add(DateTime.Today, 1.0, 1.0, 1.0, 1.0, 1.0, update: false); - s.Add(DateTime.Today, 0.0, 0.0, 0.0, 0.0, 0.0, update: true); - Assert.Equal(0.0, s[^1].o); - Assert.Equal(0.0, s[^1].h); - Assert.Equal(0.0, s[^1].l); - Assert.Equal(0.0, s[^1].c); - Assert.Equal(0.0, s[^1].v); - Assert.Equal(2, s.Count); - } - [Fact] - public void Extracting_TSeries() - { - TBars s = new() - { - { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1 }, - { DateTime.Today, 2.1, 3.1, 4.1, 5.1, 6.1 } - }; - - TSeries t = s.Open; - Assert.Equal(t.t, s.Open.t); - Assert.Equal(t.v, s.Open.v); - - t = s.High; - Assert.Equal(t.t, s.High.t); - Assert.Equal(t.v, s.High.v); - - t = s.Low; - Assert.Equal(t.t, s.Low.t); - Assert.Equal(t.v, s.Low.v); - - t = s.Close; - Assert.Equal(t.t, s.Close.t); - Assert.Equal(t.v, s.Close.v); - - t = s.Volume; - Assert.Equal(t.t, s.Volume.t); - Assert.Equal(t.v, s.Volume.v); - - t = s.HL2; - Assert.Equal(t.t, s.HL2.t); - Assert.Equal(t.v, s.HL2.v); - - t = s.OC2; - Assert.Equal(t.t, s.OC2.t); - Assert.Equal(t.v, s.OC2.v); - - t = s.OHL3; - Assert.Equal(t.t, s.OHL3.t); - Assert.Equal(t.v, s.OHL3.v); - - t = s.HLC3; - Assert.Equal(t.t, s.HLC3.t); - Assert.Equal(t.v, s.HLC3.v); - - t = s.OHLC4; - Assert.Equal(t.t, s.OHLC4.t); - Assert.Equal(t.v, s.OHLC4.v); - - t = s.HLCC4; - Assert.Equal(t.t, s.HLCC4.t); - Assert.Equal(t.v, s.HLCC4.v); - } - [Fact] - public void Broadcasting_Events() - { - TBars s = new() { (DateTime.Today, 2.1, 3.1, 4.1, 5.1, 6.1) }; - TSeries t = new(); - s.Close.Pub += t.Sub; - s.Add(DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1, false); - Assert.Equal(s.Close.v, t.v); - Assert.Equal(s.Close.Count, t.Count); - } -} diff --git a/archive/Tests/Tests.csproj b/archive/Tests/Tests.csproj deleted file mode 100644 index 13369b4e..00000000 --- a/archive/Tests/Tests.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - net7.0 - preview - enable - enable - false - AnyCPU;x64 - 0.2.1.0 - 0.2.1.0 - 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d - 0.2.1-dev.2 - $(NoWarn);NETSDK1057 - true - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/archive/Tests/Validations/Trends/Pandas_TA.cs b/archive/Tests/Validations/Trends/Pandas_TA.cs deleted file mode 100644 index 4656dc13..00000000 --- a/archive/Tests/Validations/Trends/Pandas_TA.cs +++ /dev/null @@ -1,484 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; -using Python.Runtime; - -namespace Validations; - -public class PandasTA : IDisposable -{ - private bool disposed = false; - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, skip; - private readonly int digits; - private readonly dynamic np; - private readonly dynamic ta; - private readonly dynamic pd; - private readonly dynamic df; - - public PandasTA() - { - bars = new GBM_Feed(5000, 0.8, 0.0); - period = rnd.Next(28) + 3; - skip = period + 50; - digits = 8; - - var pythonDLL = PythonLibrary.Locate(); - Runtime.PythonDLL = pythonDLL; - PythonEngine.Initialize(); - - np = Py.Import("numpy"); - pd = Py.Import("pandas"); - ta = Py.Import("pandas_ta"); - - string[] cols = { "open", "high", "low", "close", "volume" }; - var ary = new double[bars.Count, 5]; - for (var i = 0; i < bars.Count; i++) - { - ary[i, 0] = bars.Open[i].v; - ary[i, 1] = bars.High[i].v; - ary[i, 2] = bars.Low[i].v; - ary[i, 3] = bars.Close[i].v; - ary[i, 4] = bars.Volume[i].v; - } - - df = ta.DataFrame(data: np.array(ary), index: np.array(bars.Close.t), columns: np.array(cols)); - } - - public void Dispose() - { - Dispose(true); - PythonEngine.Shutdown(); - GC.SuppressFinalize(this); - } - - ~PandasTA() - { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - if (!disposed) - { - disposed = true; - } - } - - [Fact] - private 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 (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void BBANDS() - { - BBANDS_Series QL = new(bars.Close, period); - var pta = df.ta.bbands(close: df.close, length: period).to_numpy(); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL.Lower[i].v; - var PanTA_item = (double)pta[i][0]; //lower - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Mid[i].v; - PanTA_item = (double)pta[i][1]; //mid - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Upper[i].v; - PanTA_item = (double)pta[i][2]; //upper - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void BIAS() - { - BIAS_Series QL = new(bars.Close, period, false); - var pta = df.ta.bias(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void CCI() - { - CCI_Series QL = new(bars, period, false); - var pta = df.ta.cci(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void DEMA() - { - DEMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.dema(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void EMA() - { - EMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.ema(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void ENTROPY() - { - ENTROPY_Series QL = new(bars.Close, period, false); - var pta = df.ta.entropy(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void HL2() - { - var pta = df.ta.hl2(high: df.high, low: df.low); - for (var i = bars.HL2.Length - 1; i > skip; i--) - { - var QL_item = bars.HL2[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void HLC3() - { - var pta = df.ta.hlc3(high: df.high, low: df.low, close: df.close); - for (var i = bars.HLC3.Length; i > skip; i--) - { - var QL_item = bars.HLC3[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void HMA() - { - HMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.hma(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void KURTOSIS() - { - KURTOSIS_Series QL = new(bars.Close, period, false); - var pta = df.ta.kurtosis(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void MACD() - { - MACD_Series QL = new(bars.Close, 26, 12, 9, false); - var pta = df.ta.macd(close: df.close).to_numpy(); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1][0]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Signal[i - 1].v; - PanTA_item = (double)pta[i - 1][2]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void MAD() - { - MAD_Series QL = new(bars.Close, period, false); - var pta = df.ta.mad(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void MEDIAN() - { - MEDIAN_Series QL = new(bars.Close, period); - var pta = df.ta.median(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void OBV() - { - OBV_Series QL = new(bars); - var pta = df.ta.obv(close: df.close, volume: df.volume); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void OHLC4() - { - var pta = df.ta.ohlc4(open: df.open, high: df.high, low: df.low, close: df.close); - for (var i = bars.OHLC4.Length; i > skip; i--) - { - var QL_item = bars.OHLC4[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SDEV() - { - SDEV_Series QL = new(bars.Close, period, false); - var pta = df.ta.stdev(close: df.close, length: period, ddof: 0); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SMA() - { - SMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.sma(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SSDEV() - { - SSDEV_Series QL = new(bars.Close, period, false); - var pta = df.ta.stdev(close: df.close, length: period, ddof: 1); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SVARIANCE() - { - SVAR_Series QL = new(bars.Close, period); - var pta = df.ta.variance(close: df.close, length: period, ddof: 1); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void TEMA() - { - TEMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.tema(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void TR() - { - TR_Series QL = new(bars); - var pta = df.ta.true_range(high: df.high, low: df.low, close: df.close); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void TRIMA() - { - // TODO: return length to variable length (period) when Pandas-TA fixes trima to calculate even periods right - TRIMA_Series QL = new(bars.Close, 11); - var pta = df.ta.trima(close: df.close, length: 11); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void VARIANCE() - { - VAR_Series QL = new(bars.Close, period); - var pta = df.ta.variance(close: df.close, length: period, ddof: 0); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void WMA() - { - WMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.wma(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void ZSCORE() - { - ZSCORE_Series QL = new(bars.Close, period, false); - var pta = df.ta.zscore(close: df.close, length: period, ddof: 0); - for (var i = QL.Length - 1; i > skip; i--) - { - var QL_item = QL[i - 1].v; - var PanTA_item = (double)pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } -} - -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)) - { - return "/usr/lib/x86_64-linux-gnu/libpython3.10.so"; - /* - List pythonLibraries = new List(); - List directoriesToSearch = new List { "/home/runner/.local/lib" }; // 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/archive/Tests/Validations/Trends/Skender.cs b/archive/Tests/Validations/Trends/Skender.cs deleted file mode 100644 index 393f55f1..00000000 --- a/archive/Tests/Validations/Trends/Skender.cs +++ /dev/null @@ -1,489 +0,0 @@ -using System; -using QuanTAlib; -using Skender.Stock.Indicators; -using Xunit; - -namespace Validations; -public class Skender -{ - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, digits, skip; - private readonly IEnumerable quotes; - - - public Skender() - { - bars = new(Bars: 10000, Volatility: 0.5, Drift: 0.0, Precision: 2); - period = rnd.Next(30) + 5; - digits = 6; //minimizing rounding errors in type conversions - skip = period + 2; - - quotes = bars.Select(q => new Quote - { - Date = q.t, - Open = (decimal)q.o, - High = (decimal)q.h, - Low = (decimal)q.l, - Close = (decimal)q.c, - Volume = (decimal)q.v - }); - } - - /* - [Fact] - public void ADL() - { - ADL_Series QL = new(bars); - var SK = quotes.GetAdl().Select(i => i.Adl); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1)!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - */ - [Fact] - public void ALMA() - { - ALMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetAlma(period).Select(i => i.Alma.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void ATR() - { - ATR_Series QL = new(bars, period: period, useNaN: false); - var SK = quotes.GetAtr(period).Select(i => i.Atr.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void ATRP() - { - ATRP_Series QL = new(bars, period, false); - var SK = quotes.GetAtr(period).Select(i => i.Atrp.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void BBANDS() - { - BBANDS_Series QL = new(bars.Close, period, 2.0, useNaN: false); - var SK = quotes.GetBollingerBands(period, 2.0); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL.Mid[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Sma!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Upper[i - 1].v; - SK_item = SK.ElementAt(i - 1).UpperBand!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Lower[i - 1].v; - SK_item = SK.ElementAt(i - 1).LowerBand!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Bandwidth[i - 1].v; - SK_item = SK.ElementAt(i - 1).Width!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.PercentB[i - 1].v; - SK_item = SK.ElementAt(i - 1).PercentB!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Zscore[i - 1].v; - SK_item = SK.ElementAt(i - 1).ZScore!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void CCI() - { - CCI_Series QL = new(bars, period, false); - var SK = quotes.GetCci(period).Select(i => i.Cci.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void CMO() - { - CMO_Series QL = new(bars.Close, period, false); - var SK = quotes.GetCmo(period).Select(i => i.Cmo.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void CORR() - { - CORR_Series QL = new(bars.High, bars.Low, period, false); - var SK = quotes.Use(CandlePart.High).GetCorrelation(quotes.Use(CandlePart.Low), period).Select(i => i.Correlation.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void COVAR() - { - COVAR_Series QL = new(bars.High, bars.Low, period, false); - var SK = quotes.Use(CandlePart.High).GetCorrelation(quotes.Use(CandlePart.Low), period).Select(i => i.Covariance.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void DEMA() - { - DEMA_Series QL = new(bars.Close, period, false, useSMA: true); - var SK = quotes.GetDema(period).Select(i => i.Dema.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void EMA() - { - EMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetEma(lookbackPeriods: period).Select(i => i.Ema.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void HL2() - { - TSeries QL = bars.HL2; - var SK = quotes.GetBaseQuote(CandlePart.HL2).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void HLC3() - { - TSeries QL = bars.HLC3; - var SK = quotes.GetBaseQuote(CandlePart.HLC3).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void HMA() - { - HMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetHma(period).Select(i => i.Hma.Null2NaN()!); - for (int i = QL.Length; i > skip * 2; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - - [Fact] - public void KAMA() - { - // TODO: check precision of KAMA() - KAMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetKama(period).Select(i => i.Kama.Null2NaN()!); - for (int i = QL.Length; i > skip + 2; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void SLOPE() - { - SLOPE_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetSlope(period); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = (double)SK.ElementAt(i - 1).Slope!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Intercept[i - 1].v; - SK_item = (double)SK.ElementAt(i - 1).Intercept!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.RSquared[i - 1].v; - SK_item = (double)SK.ElementAt(i - 1).RSquared!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.StdDev[i - 1].v; - SK_item = (double)SK.ElementAt(i - 1).StdDev!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void MACD() - { - MACD_Series QL = new(bars.Close, 26, 12, 9, useNaN: false); - var SK = quotes.GetMacd(12, 26, 9); - for (int i = QL.Length; i > 27; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Macd.Null2NaN()!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - //QL_item = QL.Signal[i - 1].v; - //SK_item = SK.ElementAt(i - 1).Signal.Null2NaN()!; - //Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void MAD() - { - MAD_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mad.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void MAMA() - { - MAMA_Series QL = new(bars.HL2, fastlimit: 0.5, slowlimit: 0.05); - var SK = quotes.GetMama(fastLimit: 0.5, slowLimit: 0.05); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Mama.Null2NaN()!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Fama[i - 1].v; - SK_item = SK.ElementAt(i - 1).Fama.Null2NaN()!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void MAPE() - { - MAPE_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mape.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void MSE() - { - MSE_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mse.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void OBV() - { - OBV_Series QL = new(bars, period, false); - var SK = quotes.GetObv(period).Select(i => i.Obv!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL.Last().v; - // adding volume[0] to OBV to pass the test and keep compatibility with TA-LIB - double SK_item = SK.Last()! + (double)quotes.First().Volume!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void OC2() - { - TSeries QL = bars.OC2; - var SK = quotes.GetBaseQuote(CandlePart.OC2).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void OHL3() - { - TSeries QL = bars.OHL3; - var SK = quotes.GetBaseQuote(CandlePart.OHL3).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void OHLC4() - { - TSeries QL = bars.OHLC4; - var SK = quotes.GetBaseQuote(CandlePart.OHLC4).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void RSI() - { - RSI_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetRsi(period).Select(i => i.Rsi.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void SDEV() - { - SDEV_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetStdDev(period).Select(i => i.StdDev.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void SMA() - { - SMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSma(period).Select(i => i.Sma.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void SMMA() - { - SMMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetSmma(period).Select(i => i.Smma.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void T3() - { - T3_Series QL = new(source: bars.Close, period: period, vfactor: 0.7, false); - var SK = quotes.GetT3(lookbackPeriods: period, volumeFactor: 0.7).Select(i => i.T3.Null2NaN()!); - for (int i = QL.Length; i > period * 15; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void TRIX() - { - TRIX_Series QL = new(bars.Close, period, false); - var SK = quotes.GetTrix(period).Select(i => i.Trix.Null2NaN()!); - for (int i = QL.Length; i > period * 12; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void TEMA() - { - TEMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetTema(period).Select(i => i.Tema.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void TR() - { - TR_Series QL = new(bars); - var SK = quotes.GetTr().Select(i => i.Tr.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void WMA() - { - WMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetWma(period).Select(i => i.Wma.Null2NaN()!); - for (int i = QL.Length; i > skip * 2; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void ZSCORE() - { - ZSCORE_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetStdDev(period).Select(i => i.ZScore.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - -} diff --git a/archive/Tests/Validations/Trends/TA_LIB.cs b/archive/Tests/Validations/Trends/TA_LIB.cs deleted file mode 100644 index 0e2c3654..00000000 --- a/archive/Tests/Validations/Trends/TA_LIB.cs +++ /dev/null @@ -1,487 +0,0 @@ -using Xunit; -using System; -using TALib; -using QuanTAlib; - -namespace Validations; -public class Ta_Lib -{ - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, digits, skip; - private readonly double[] TALIB; - private readonly double[] TALIB2; - private readonly double[] inopen; - private readonly double[] inhigh; - private readonly double[] inlow; - private readonly double[] inclose; - private readonly double[] involume; - - public Ta_Lib() - { - bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3); - period = rnd.Next(28) + 3; - skip = period + 2; - digits = 9; - - TALIB = new double[bars.Count]; - TALIB2 = new double[bars.Count]; - inopen = bars.Open.v.ToArray(); - inhigh = bars.High.v.ToArray(); - inlow = bars.Low.v.ToArray(); - inclose = bars.Close.v.ToArray(); - involume = bars.Volume.v.ToArray(); - } - - [Fact] - public void ADD() - { - ADD_Series QL = new(bars.Open, bars.Close); - Core.Add(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADL() - { - ADL_Series QL = new(bars); - Core.Ad(inhigh, inlow, inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > 0; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADOSC() - { - ADOSC_Series QL = new(bars, 3, 10, false); - Core.AdOsc(inhigh, inlow, inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip * 2; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ATR() - { - ATR_Series QL = new(bars, period: period, useNaN: false); - Core.Atr(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - public void BBANDS() - { - double[] outMiddle = new double[bars.Count]; - double[] outUpper = new double[bars.Count]; - double[] outLower = new double[bars.Count]; - BBANDS_Series QL = new(bars.Close, period: period, multiplier: 2.0, false); - Core.Bbands(inclose, 0, bars.Count - 1, outRealUpperBand: outUpper, outRealMiddleBand: outMiddle, outRealLowerBand: outLower, out int outBegIdx, out _, optInTimePeriod: period, optInNbDevUp: 2.0, optInNbDevDn: 2.0); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL.Upper[i].v; - double TA_item = outUpper[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); - QL_item = QL.Mid[i].v; - TA_item = outMiddle[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); - QL_item = QL.Lower[i].v; - TA_item = outLower[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); - } - } - [Fact] - public void CCI() - { - CCI_Series QL = new(bars, period, false); - Core.Cci(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* CMO in TA-LIB is not valid - [Fact] - public void CMO() { - CMO_Series QL = new(bars.Close, period, false); - Core.Cmo(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - */ - [Fact] - public void CORR() - { - CORR_Series QL = new(bars.Open, bars.Close, period); - Core.Correl(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DEMA() - { - DEMA_Series QL = new(bars.Close, period, false, useSMA: false); - Core.Dema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > period * 10; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DIV() - { - DIV_Series QL = new(bars.Open, bars.Close); - Core.Div(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void EMA() - { - EMA_Series QL = new(bars.Close, period, false); - Core.Ema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HL2() - { - TSeries QL = bars.HL2; - Core.MedPrice(inhigh, inlow, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLC3() - { - TSeries QL = bars.HLC3; - Core.TypPrice(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLCC4() - { - TSeries QL = bars.HLCC4; - Core.WclPrice(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void KAMA() - { - KAMA_Series QL = new(bars.Close, period, fast: 2, slow: 30); - Core.Kama(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outReal: TALIB, outBegIdx: out int outBegIdx, outNbElement: out _, optInTimePeriod: period); - for (int i = QL.Length - 1; i > skip * 15; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MACD() - { - double[] macdSignal = new double[bars.Count]; - double[] macdHist = new double[bars.Count]; - MACD_Series QL = new(bars.Close, slow: 26, fast: 12, signal: 9, false); - // TA-LIB runs EMA without SMA, leaving first 100 values for convergence - Core.Macd(inclose, 0, bars.Count - 1, outMacd: TALIB, outMacdSignal: macdSignal, outMacdHist: macdHist, out int outBegIdx, out _, optInFastPeriod: 12, optInSlowPeriod: 26, optInSignalPeriod: 9); - for (int i = QL.Length - 1; i > 100; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Signal[i].v; - TA_item = macdSignal[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* - [Fact] - public void MAMA() - { - MAMA_Series QL = new(bars.Close, fastlimit: 0.5, slowlimit: 0.05); - Core.Mama(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outMama: TALIB, outFama: TALIB2, outBegIdx: out int outBegIdx, outNbElement: out _, optInFastLimit: 0.5, optInSlowLimit: 0.05); - for (int i = QL.Length - 1; i > skip * 10; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits-1), Math.Exp(-digits-1)); - } - } - */ - [Fact] - public void MAX() - { - MAX_Series QL = new(bars.Close, period, false); - Core.Max(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIDPOINT() - { - MIDPOINT_Series QL = new(bars.Close, period, false); - Core.MidPoint(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIDPRICE() - { - MIDPRICE_Series QL = new(bars, period, false); - Core.MidPrice(inhigh, inlow, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIN() - { - MIN_Series QL = new(bars.Close, period, false); - Core.Min(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MUL() - { - MUL_Series QL = new(bars.Open, bars.Close); - Core.Mult(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OBV() - { - OBV_Series QL = new(bars, period, false); - Core.Obv(inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OHLC4() - { - TSeries QL = bars.OHLC4; - Core.AvgPrice(inopen, inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void RSI() - { - RSI_Series QL = new(bars.Close, period, false); - Core.Rsi(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SDEV() - { - SDEV_Series QL = new(bars.Close, period, false); - Core.StdDev(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SMA() - { - SMA_Series QL = new(bars.Close, period, false); - Core.Sma(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUB() - { - SUB_Series QL = new(bars.Open, bars.Close); - Core.Sub(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUM() - { - CUSUM_Series QL = new(bars.Close, period, false); - Core.Sum(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void T3() - { - T3_Series QL = new(source: bars.Close, period: period, vfactor: 0.7, useNaN: false); - Core.T3(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outReal: TALIB, outBegIdx: out int outBegIdx, outNbElement: out _, optInTimePeriod: period, optInVFactor: 0.7); - for (int i = QL.Length - 1; i > period * 10; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TEMA() - { - TEMA_Series QL = new(bars.Close, period, false); - Core.Tema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip * 15; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TR() - { - TR_Series QL = new(bars); - Core.TRange(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TRIMA() - { - TRIMA_Series QL = new(bars.Close, period, false); - Core.Trima(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TRIX() - { - TRIX_Series QL = new(bars.Close, period, useNaN: false, useSMA: true); - Core.Trix(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > period * 10; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void VAR() - { - VAR_Series QL = new(bars.Close, period, false); - Core.Var(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip * 15; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void WMA() - { - WMA_Series QL = new(bars.Close, period, false); - Core.Wma(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - -} diff --git a/archive/Tests/Validations/Trends/Tulip.cs b/archive/Tests/Validations/Trends/Tulip.cs deleted file mode 100644 index fc36b359..00000000 --- a/archive/Tests/Validations/Trends/Tulip.cs +++ /dev/null @@ -1,578 +0,0 @@ -using Xunit; -using System; -using Tulip; -using QuanTAlib; - -namespace Validations; -public class Tulip_Test -{ - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, digits, skip; - private readonly double[] outdata; - private readonly double[] inopen; - private readonly double[] inhigh; - private readonly double[] inlow; - private readonly double[] inclose; - private readonly double[] involume; - - public Tulip_Test() - { - bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3); - period = rnd.Next(28) + 3; - skip = period + 5; - digits = 8; - - outdata = new double[bars.Count]; - inopen = bars.Open.v.ToArray(); - inhigh = bars.High.v.ToArray(); - inlow = bars.Low.v.ToArray(); - inclose = bars.Close.v.ToArray()!; - involume = bars.Volume.v.ToArray()!; - - } - [Fact] - public void ADL() - { - double[][] arrin = { inhigh, inlow, inclose, involume }; - double[][] arrout = { outdata }; - ADL_Series QL = new(bars); - Tulip.Indicators.ad.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADD() - { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - ADD_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.add.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADOSC() - { - double[][] arrin = { inhigh, inlow, inclose, involume }; - double[][] arrout = { outdata }; - int s = 3; - ADOSC_Series QL = new(bars, s, period, false); - Tulip.Indicators.adosc.Run(inputs: arrin, options: new double[] { s, period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ATR() - { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - ATR_Series QL = new(bars, period: period, useNaN: false); - Tulip.Indicators.atr.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - //Tulip ATR doesn't use warm-up SMA, compensating with 200 warming bars - for (int i = QL.Length - 1; i > 200 + skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void BBANDS() - { - double[][] arrin = { inclose }; - double[] outmid = new double[bars.Count]; - double[] outlower = new double[bars.Count]; - double[] outupper = new double[bars.Count]; - double[][] arrout = { outlower, outmid, outupper }; - BBANDS_Series QL = new(bars.Close, period, 2, false); - Tulip.Indicators.bbands.Run(inputs: arrin, options: new double[] { period, 2 }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL.Lower[i].v; - double TU_item = outlower[i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Mid[i].v; - TU_item = outmid[i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Upper[i].v; - TU_item = outupper[i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* - [Fact] - public void CCI() { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - CCI_Series QL = new(bars, period, useNaN: false); - Tulip.Indicators.cci.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = outdata[i - period + 1]; - Assert.Equal(QL_item,TU_item); - //Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - */ - [Fact] - public void CMO() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - CMO_Series QL = new(bars.Close, period, useNaN: false); - Tulip.Indicators.cmo.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DECAY() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - DECAY_Series QL = new(bars.Close, period, useNaN: false); - Tulip.Indicators.decay.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 200; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DEMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - DEMA_Series QL = new(bars.Close, period, useNaN: false, useSMA: false); - Tulip.Indicators.dema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 200; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - (period + period - 2)]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DIV() - { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - DIV_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.div.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void EDECAY() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - DECAY_Series QL = new(bars.Close, period, exponential: true, useNaN: false); - Tulip.Indicators.edecay.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 200; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void EMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - // Tulip EMA doesn't use SMA to warm-up - EMA_Series QL = new(bars.Close, period, false, useSMA: false); - Tulip.Indicators.ema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HL2() - { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - - TSeries QL = bars.HL2; - Tulip.Indicators.medprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLC3() - { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - TSeries QL = bars.HLC3; - Tulip.Indicators.typprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLCC4() - { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - TSeries QL = bars.HLCC4; - Tulip.Indicators.wcprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - public void HMA() - { - int p = 10; - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - HMA_Series QL = new(bars.Close, p, false); - Tulip.Indicators.hma.Run(inputs: arrin, options: new double[] { p }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 2; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - p - 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits - 2), Math.Exp(-digits - 2)); - } - } - - [Fact] - public void KAMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - KAMA_Series QL = new(bars.Close, period); - Tulip.Indicators.kama.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > 250; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - public void LINREG() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - SLOPE_Series QL = new(bars.Close, period); - Tulip.Indicators.linregslope.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MACD() - { - - double[] outsignal = new double[bars.Count]; - double[] outhist = new double[bars.Count]; - double[][] arrin = { inclose }; - double[][] arrout = { outdata, outsignal, outhist }; - MACD_Series QL = new(bars.Close, slow: 26, fast: 10, signal: 9); - Tulip.Indicators.macd.Run(inputs: arrin, options: new double[] { 10, 26, 9 }, outputs: arrout); - for (int i = QL.Length - 1; i > 150; i--) - { - double QL_item = QL[i].v; - double TU_item = outdata[i - 26 + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MAX() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - MAX_Series QL = new(bars.Close, period, false); - Tulip.Indicators.max.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIN() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - MIN_Series QL = new(bars.Close, period, false); - Tulip.Indicators.min.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MUL() - { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - MUL_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.mul.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OBV() - { - double[][] arrin = { inclose, involume }; - double[][] arrout = { outdata }; - OBV_Series QL = new(bars, period, false); - Tulip.Indicators.obv.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i] + arrin[1][0]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OHLC4() - { - double[][] arrin = { inopen, inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - TSeries QL = bars.OHLC4; - Tulip.Indicators.avgprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void RMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - RMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.wilders.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void RSI() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - RSI_Series QL = new(bars.Close, period, false); - Tulip.Indicators.rsi.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - SMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.sma.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SDEV() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - SDEV_Series QL = new(bars.Close, period, false); - Tulip.Indicators.stddev.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUB() - { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - SUB_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.sub.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUM() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - CUSUM_Series QL = new(bars.Close, period, false); - Tulip.Indicators.sum.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TR() - { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - TR_Series QL = new(bars); - Tulip.Indicators.tr.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TEMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - TEMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.tema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 200; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - (period - 1) * 3]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TRIMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - TRIMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.trima.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* - [Fact] - public void TRIX() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - TRIX_Series QL = new(bars.Close, period); - Tulip.Indicators.trix.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > period+200; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - (period*3) + 2]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits+2), Math.Exp(-digits+2)); - } - } - */ - [Fact] - public void VAR() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - VAR_Series QL = new(bars.Close, period, false); - Tulip.Indicators.var.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void WMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - WMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.wma.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ZLEMA() - { - int p = 4; - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - ZLEMA_Series QL = new(bars.Close, p, false); - Tulip.Indicators.zlema.Run(inputs: arrin, options: new double[] { p }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 20; i--) - { - double QL_item = QL[i].v; - double TU_item = outdata[i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits - 2), Math.Exp(-digits - 2)); - } - } -} diff --git a/archive/Tests/requirements.txt b/archive/Tests/requirements.txt deleted file mode 100644 index 2cc91dd6..00000000 --- a/archive/Tests/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -pytz -six -python-dateutil -numpy -pandas -pandas-ta \ No newline at end of file diff --git a/archive/docs/.nojekyll b/archive/docs/.nojekyll deleted file mode 100644 index 8b137891..00000000 --- a/archive/docs/.nojekyll +++ /dev/null @@ -1 +0,0 @@ - diff --git a/archive/docs/ALMA.md b/archive/docs/ALMA.md deleted file mode 100644 index 0aa250a0..00000000 --- a/archive/docs/ALMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# ALMA: Arnaud Legoux Moving Average - -period = 10 - -![Alt text](./img/ALMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/DEMA.md b/archive/docs/DEMA.md deleted file mode 100644 index e82d1ef9..00000000 --- a/archive/docs/DEMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# DEMA: Double Exponential Moving Average - -period = 10 - -![Alt text](./img/DEMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/DWMA.md b/archive/docs/DWMA.md deleted file mode 100644 index 9ba59690..00000000 --- a/archive/docs/DWMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# DWMA: Double Weighted Moving Average - -period = 10 - -![Alt text](./img/DWMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/EMA.md b/archive/docs/EMA.md deleted file mode 100644 index ba8091d8..00000000 --- a/archive/docs/EMA.md +++ /dev/null @@ -1,50 +0,0 @@ -# EMA: Exponential Moving Average - -Also known as exponentially weighted moving average, as it places greater weight on the most recent data points. -EMA reacts more agressively to recent data changes and calculates the current value using just the previous EMA value and current data point. The weight applied to the new value is typically $k = 2 / (period-1)$ - -## Calculation - -EMA is a rolling calculation requiring only one historical data point to calculate the current value and is denoted as ${EMA}_{p}{(data)}$ where $p$ represents the period and $data$ represents the list of data points. - -Some implementations of EMA calculate a seeding value of $EMA$ as a ${SMA}_{p}$ when $n < period$ - and start the $EMA$ calculation only after the warm-up period. QuanTAlib offers an option to enable/disable SMA warm-up. - -$$ -EMA_n = \left\{ \begin{array}{cl} -\frac{1}{p}\left( data_{n}-data_{n-p}\right)+SMA_{n-1} & : \ n \leq period \\ -{k}\times ({data_{n}} - EMA_{n-1}) + EMA_{n-1} & : \ n > period -\end{array} \right. -$$ - -## Behavior -![Alt text](./img/EMA_chart.svg) - -## Reference Calculation -period = 5 -``` -TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; -EMA_Series ema = new(data, 5, useNaN: false); -EMA_Series ema_nan = new(data, 5, useNaN: true); -for (int i=0; i< data.Count; i++) - Console.WriteLine($"{i}\t{data[i].v,7:f2}\t{ema_nan[i].v,7:f3}\t{ema[i].v,7:f3}"); -``` -| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ | -|--|:--:|:--:|:--:|:--:|:--:|:--:| -|0| 81.59| **81.590**| _NaN_| _NaN_| _NaN_| _NaN_| -|1| 81.06| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_| -|2| 82.87| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_| -|3| 83.00| **82.130**| _NaN_| _NaN_| _NaN_| _NaN_| -|4| 83.61| **82.426**| _82.426_| _82.426_| _82.426_| _82.426_| -|5| 83.15| **82.667**| _82.667_| _82.667_| _82.667_|_82.667_| -|6| 82.84| **82.725**| _82.725_| _82.725_| _82.725_|_82.725_| -|7| 83.99| **83.147**| _83.147_| _83.147_| _83.147_|_83.147_| -|8| 84.55| **83.614**| _83.614_| _83.614_| _83.614_|_83.614_| -|9| 84.36| **83.863**| _83.863_| _83.863_| _83.863_|_83.863_| -|10| 85.53| **84.419**| _84.419_| _84.419_| _84.419_|_84.419_| -|11| 86.54| **85.126**| _85.126_| _85.126_| _85.126_|_85.126_| -|12| 86.89| **85.714**| _85.714_| _85.714_| _85.714_|_85.714_| -|13| 87.77| **86.399**| _86.399_| _86.399_| _86.399_|_86.399_| -|14| 87.29| **86.696**| _86.696_| _86.696_| _86.696_|_86.696_| -## References - -- https://en.wikipedia.org/wiki/Exponential_smoothing \ No newline at end of file diff --git a/archive/docs/FMA.md b/archive/docs/FMA.md deleted file mode 100644 index 50b42426..00000000 --- a/archive/docs/FMA.md +++ /dev/null @@ -1,4 +0,0 @@ -# FMA: Fibonacci Moving Average -period = 6 - -![Alt text](./img/FMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/HEMA.md b/archive/docs/HEMA.md deleted file mode 100644 index 5ede117a..00000000 --- a/archive/docs/HEMA.md +++ /dev/null @@ -1,4 +0,0 @@ -# HEMA: Hull-Exponential Moving Average -period = 10 - -![Alt text](./img/HEMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/HMA.md b/archive/docs/HMA.md deleted file mode 100644 index 22761879..00000000 --- a/archive/docs/HMA.md +++ /dev/null @@ -1,4 +0,0 @@ -# HMA: Hull Moving Average -period = 10 - -![Alt text](./img/HMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/HWMA.md b/archive/docs/HWMA.md deleted file mode 100644 index 6102933e..00000000 --- a/archive/docs/HWMA.md +++ /dev/null @@ -1,4 +0,0 @@ -# HWMA: Holt-Winter Moving Average -nA = 0.5; nB = 0.3; nC = 0.01; - -![Alt text](./img/HWMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/JMA.md b/archive/docs/JMA.md deleted file mode 100644 index 31c7de3e..00000000 --- a/archive/docs/JMA.md +++ /dev/null @@ -1,4 +0,0 @@ -# JMA: Jurik Moving Average -period = 10 - -![Alt text](./img/JMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/KAMA.md b/archive/docs/KAMA.md deleted file mode 100644 index 594d4d10..00000000 --- a/archive/docs/KAMA.md +++ /dev/null @@ -1,4 +0,0 @@ -# KAMA: Kaufman's Adaptive Moving Average -period = 10 - -![Alt text](./img/KAMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/MAMA.md b/archive/docs/MAMA.md deleted file mode 100644 index 19dacbb0..00000000 --- a/archive/docs/MAMA.md +++ /dev/null @@ -1,4 +0,0 @@ -# MAMA: MESA Adaptive Moving Average -period = 10 - -![Alt text](./img/MAMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/QA.md b/archive/docs/QA.md deleted file mode 100644 index 3e56fcda..00000000 --- a/archive/docs/QA.md +++ /dev/null @@ -1,40 +0,0 @@ -### Is QuanTAlib fast? - -Well, _no_, but actually *yes*. QuanTAlib works on an additive principle, meaning that even when served a full list of quotes, QuanTAlib will process them one item at the time, rolling forward throug the given time series of data. -- If the last bar is still forming (parameter `update: true`), QuanTAlib can easily recalculates the last entry as often as needed without any need to recalculate the history. -- If a new bar is added to the input, QuanTAlib will process that one item (default parameter `update: false`) and add that one result to the List. No recalculation of the history needed. - -So, if you test QuanTAlib with small set of 500 historical bars and calculate EMA(20) on it, the performance of QuanTAlib will be dead last compared to all other TA libraries. - -But when the system uses 10,000 or historical bars, updates the current data at every new tick, and adds a new bar every minute, QuanTAlib has no rivals; all other libraries will need to re-calculate the full length of the indicator on each update/addition to the time series, while QuanTAlib will just update the last entry or add a single new value to the series. No back calculations are performed - ever. Longer the input data series and more updates/additions it gets, the greater advantage there is for QuanTAlib. - -### Are results of QuanTAlib valid? - -QuanTAlib includes battery of tests to compare its results with four well-known and reviewed Technical Analysis libraries to assure accuracy and validity of results: - -- [TA-LIB](https://www.ta-lib.org/function.html) -- [Skender Stock Indicators](https://dotnet.stockindicators.dev/) -- [Pandas-TA](https://twopirllc.github.io/pandas-ta/) -- [Tulip Indicators](https://tulipindicators.org/) - -Not all indicators are implemented by all libraries - and sometimes results of the four reference libraries disagree with each other. Indicators that return equivalent result set to **all four** libraries are marked with ⭐ on [the list of indicators](indicators.md) - these are indicators that can be trusted most. Each verified equivalency of results is marked with ✔️, and each discrepancy is marked with ❌. - -For each discrepancy the research was done to establish the reason and to select the implementation that is the most faithful to the original description of the indicator. - -_For example, CMO indicator was described by Tushar S. Chande in his book The New Technical Trader, where he includes the example of calculating 10-day CMO on a given data. QuanTAlib, Skender.NET and Tulip libraries can all replicate the results, while TA-LIB and Pandas-TA return something very different:_ - -| #| Input | **QuanTAlib** | TA-LIB | Skender | Pandas-TA | Tulip | -|--|:--:|:--:|:--:|:--:|:--:|:--:| -| 0| 101.03|**0.00**| NaN| NaN| NaN| NaN| -| 1| 101.03|**0.00**| NaN| NaN| NaN| NaN| -| 2| 101.12|**100.00**| NaN| NaN| NaN| NaN| -| 3| 101.97|**100.00**| NaN| NaN| NaN| NaN| -| 4| 102.78|**100.00**| NaN| NaN| NaN| NaN| -| 5| 103.00|**100.00**| NaN| NaN| NaN| NaN| -| 6| 102.97|**96.87**| NaN| NaN| NaN| NaN| -| 7| 103.06|**97.01**| NaN| NaN| NaN| NaN| -| 8| 102.94|**85.91**| NaN| NaN| NaN| NaN| -| 9| 102.72|**69.23**| NaN| NaN| NaN| NaN| -|10| 102.75|**69.62**| 69.62| 69.62| ~55.22~| 69.62| -|11| 102.91|**71.43**| ~71.62~| 71.43| ~60.09~| 71.43| -|12| 102.97|**71.08**| ~72.42~| 71.08| ~61.93~| 71.08| \ No newline at end of file diff --git a/archive/docs/RMA.md b/archive/docs/RMA.md deleted file mode 100644 index e33234d9..00000000 --- a/archive/docs/RMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# RMA: wildeR Moving Average - -period = 10 - -![Alt text](./img/RMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/SMA.md b/archive/docs/SMA.md deleted file mode 100644 index c84295b5..00000000 --- a/archive/docs/SMA.md +++ /dev/null @@ -1,57 +0,0 @@ -# SMA: Simple Moving Average - -SMA is is an arithmetic moving average where the weights in SMA are **equally** distributed across the given period, resulting in a mean() of the data within the period. - -## Calculation - -SMA is a rolling calculation that is looking backwards from the position ${n}$ and is denoted as ${SMA}_{p}{(data)}$ where $p$ represents the period and $data$ represents the list of data points: -$$ -SMA_p{(data)} = \frac{1}{p}\sum_{i=n-p+1}^{n} data_i -$$ -When calculating the value of the next $SMA_{p,next}$ while knowing previous SMA values, SMA calculation can be reduced to: -$$ -SMA_{p,next} = SMA_{p,prev}+\frac{1}{p}\left( data_{n+1}-data_{n+1-p}\right) -$$ - -## Implementation -`TSeries SMA_Series (TSeries source, int period = 0, bool useNaN = false)` - -- SMA_Series returns TSeries list -- `source`: input of type TSeries; SMA_Series automatically subscribes to events of new data added to the source -- `period`: optional size of a lookback window; if set to 0, SMA calculates cumulative average across the whole source -- `useNaN`: if set to _true_, SMA_Series will hide values within the initial period with NaN (for compatibility with other libraries) - -## Behavior -![Alt text](./img/SMA_chart.svg) -## Reference Calculation & Validation -period = 5 -``` -TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; -SMA_Series sma = new(data, 5, useNaN: false); -``` - -| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ | -|--|:--:|:--:|:--:|:--:|:--:|:--:| -| 0| 212.80|**212.80**| _NaN_| _NaN_| _NaN_| _NaN_| -| 1| 214.06|**213.43**| _NaN_| _NaN_| _NaN_| _NaN_| -| 2| 213.89|**213.58**| _NaN_| _NaN_| _NaN_| _NaN_| -| 3| 214.66|**213.85**| _NaN_| _NaN_| _NaN_| _NaN_| -| 4| 213.95|**213.87**| _213.87_| _213.87_| _213.87_| _213.87_| -| 5| 213.95|**214.10**| _214.10_| _214.10_| _214.10_| _214.10_| -| 6| 214.55|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| -| 7| 214.02|**214.23**| _214.23_| _214.23_| _214.23_| _214.23_| -| 8| 214.51|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| -| 9| 213.75|**214.16**| _214.16_| _214.16_| _214.16_| _214.16_| -|10| 214.22|**214.21**| _214.21_| _214.21_| _214.21_| _214.21_| -|11| 213.43|**213.99**| _213.99_| _213.99_| _213.99_| _213.99_| -|12| 214.21|**214.02**| _214.02_| _214.02_| _214.02_| _214.02_| -|13| 213.66|**213.85**| _213.85_| _213.85_| _213.85_| _213.85_| -|14| 215.03|**214.11**| _214.11_| _214.11_| _214.11_| _214.11_| -|15| 216.89|**214.64**| _214.64_| _214.64_| _214.64_| _214.64_| -|16| 216.66|**215.29**| _215.29_| _215.29_| _215.29_| _215.29_| - - -## References - - https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average - - Kaufman, Perry J. (2013) Trading Systems and Methods - - Murphy, J. (1999) Technical Analysis of the Financial Markets \ No newline at end of file diff --git a/archive/docs/SMMA.md b/archive/docs/SMMA.md deleted file mode 100644 index 9d5946f4..00000000 --- a/archive/docs/SMMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# SMMA: Smoothed Moving Average - -period = 10 - -![Alt text](./img/SMMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/T3.md b/archive/docs/T3.md deleted file mode 100644 index ae8f7082..00000000 --- a/archive/docs/T3.md +++ /dev/null @@ -1,5 +0,0 @@ -# T3: Tillson T3 Moving Average - -period = 10 - -![Alt text](./img/T3_chart.svg) \ No newline at end of file diff --git a/archive/docs/TEMA.md b/archive/docs/TEMA.md deleted file mode 100644 index 1d79871e..00000000 --- a/archive/docs/TEMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# TEMA: Triple Exponential Moving Average - -period = 10 - -![Alt text](./img/TEMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/TRIMA.md b/archive/docs/TRIMA.md deleted file mode 100644 index 712eefb1..00000000 --- a/archive/docs/TRIMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# TRIMA: Triangular Moving Average - -period = 10 - -![Alt text](./img/TRIMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/Trading_example.ipynb b/archive/docs/Trading_example.ipynb deleted file mode 100644 index 6b1af863..00000000 --- a/archive/docs/Trading_example.ipynb +++ /dev/null @@ -1,458 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
Installed Packages
  • QuanTAlib, 0.2.1
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "#r \"nuget: QuanTAlib\"\n", - "#r \"nuget: Plotly.NET;\"\n", - "#r \"nuget: Plotly.NET.Interactive;\"\n", - "\n", - "using QuanTAlib;\n", - "using Plotly.NET;\n", - "using Plotly.NET.LayoutObjects;" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "string symbol=\"SPY\"; // we'll focus on SPY symbol\n", - "int warmup = 50; // we'll allow 50 bars to pass by with no trading - for warm-up\n", - "Yahoo_Feed bars = new(symbol,350); //collect bars of symbol from Yahoo feed\n", - "TSeries data = bars.Close; //we need just one average value - (Open+High+Low+CLose)/4\n", - "\n", - "//make a chart\n", - "var d = Chart2D.Chart.Candlestick(bars.Open.v.Skip(warmup).ToList(), bars.High.v.Skip(warmup).ToList(),\n", - "bars.Low.v.Skip(warmup).ToList(), bars.Close.v.Skip(warmup).ToList(), bars.Open.t.Skip(warmup).ToList(), symbol)\n", - " .WithSize(1200,400).WithMargin(Margin.init(30,10,40,30,1,false)).WithXAxisRangeSlider(RangeSlider.init(Visible:false)).WithTitle(symbol);\n", - "d" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "JMA_Series fastMA = new(data,12); //typically MACD uses EMA(12), but let's try with superior JMA(12)\n", - "JMA_Series slowMA = new(data,26); //likewise, let's use JMA(26) instead of EMA(26)\n", - "\n", - "//make a chart\n", - "var cfast = Chart2D.Chart.Line(fastMA.t.Skip(warmup).ToList(), fastMA.v.Skip(warmup).ToList(), false, \"Fast MA\").WithLineStyle(Width: 2, Color: Color.fromString(\"blue\"));\n", - "var cslow = Chart2D.Chart.Line(slowMA.t.Skip(warmup).ToList(), slowMA.v.Skip(warmup).ToList(), false, \"Slow MA\").WithLineStyle(Width: 2, Color: Color.fromString(\"red\"));\n", - "var chart = Chart.Combine(new []{cfast,cslow}).WithSize(1200,400).WithMargin(Margin.init(30,10,40,30,1,false))\n", - " .WithXAxisRangeSlider(RangeSlider.init(Visible:false)).WithTitle($\"slow MA and fast MA of {symbol} OHLC4\");\n", - "chart" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "SUB_Series MACD_line = new(fastMA, slowMA); //MACD line is just a SUBtraction fastMA-slowMA\n", - "JMA_Series signal_line = new(MACD_line, 9); //signal line is an EMA(9) of MACD; we use superior JMA(9) instead\n", - "\n", - "//make a chart\n", - "var cfast = Chart2D.Chart.Line(MACD_line.t.Skip(warmup).ToList(), MACD_line.v.Skip(warmup).ToList(), false, \"MACD\").WithLineStyle(Width: 2, Color: Color.fromString(\"orange\"));\n", - "var cslow = Chart2D.Chart.Line(signal_line.t.Skip(warmup).ToList(), signal_line.v.Skip(warmup).ToList(), false, \"signal\").WithLineStyle(Width: 2, Color: Color.fromString(\"green\"));\n", - "var chart = Chart.Combine(new []{cfast,cslow}).WithSize(1200,400).WithMargin(Margin.init(30,10,40,30,1,false))\n", - " .WithXAxisRangeSlider(RangeSlider.init(Visible:false)).WithTitle($\"MACD line and signal line of fastMA-slowMA\");\n", - "chart" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "SUB_Series histogram = new(MACD_line, signal_line); //MACD histogram is an oscillator of MACD-signal\n", - "\n", - "//make a chart\n", - "var cfast = Chart2D.Chart.Column(values: histogram.v.Skip(warmup).ToList(), Keys: histogram.t.Skip(warmup).ToList())\n", - ".WithSize(1200,400).WithMargin(Margin.init(30,10,40,30,1,false)).WithXAxisRangeSlider(RangeSlider.init(Visible:false))\n", - ".WithTitle(\"MACD histogram of MACD-signal\");\n", - "cfast" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "COMPARE_Series over = new(histogram,0); //generate over/under series when histogram is above/below zero\n", - "CROSS_Series trades = new(histogram,0); //generate a signal series where histogram crosses zero (from below and from above)\n", - "\n", - "//make a chart\n", - "var cover = Chart2D.Chart.Line(over.t.Skip(warmup).ToList(),over.v.Skip(warmup).ToList(),false,\"state\").WithLineStyle(Width: 1, Color: Color.fromString(\"blue\"));\n", - "var cbars = Chart2D.Chart.Area(trades.t.Skip(warmup).ToList(), trades.v.Skip(warmup).ToList(),false );\n", - "var chart = Chart.Combine(new []{cover,cbars}).WithSize(1200,400).WithMargin(Margin.init(30,10,40,30,1,false))\n", - " .WithXAxisRangeSlider(RangeSlider.init(Visible:false)).WithTitle(\"in-market signal and trading orders based on MACD histogram\");\n", - "chart" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "EQUITY_Series folio = new(trades, data, Long:true, Short:false, Warmup:warmup); //generate equity curve from trades and\n", - "\n", - "//make a chart\n", - "var cbars = Chart2D.Chart.Area(folio.t.Skip(warmup).ToList(), folio.v.Skip(warmup).ToList(),false ).WithSize(1200,400).WithMargin(Margin.init(30,10,40,30,1,false))\n", - " .WithXAxisRangeSlider(RangeSlider.init(Visible:false)).WithTitle($\"Equity curve (long trades only) for MACD on {symbol}\");\n", - "cbars" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
107.93999999999998
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "// some what-if examples\n", - "string symbol=\"IBM\";\n", - "int warmup = 50;\n", - "Yahoo_Feed bars = new(symbol,350);\n", - "TSeries data = bars.Close;\n", - "HEMA_Series fastMA = new(data,12);\n", - "JMA_Series slowMA = new(data,23,-100);\n", - "SUB_Series MACD_line = new(fastMA, slowMA);\n", - "JMA_Series signal_line = new(MACD_line, 9);\n", - "SUB_Series histogram = new(MACD_line, signal_line);\n", - "CROSS_Series trades = new(histogram,0);\n", - "EQUITY_Series folio = new(trades, data, Long:true, Short:false, Warmup:warmup);\n", - "folio[^1].v" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".NET (C#)", - "language": "C#", - "name": ".net-csharp" - }, - "language_info": { - "name": "polyglot-notebook" - }, - "polyglot_notebook": { - "kernelInfo": { - "defaultKernelName": "csharp", - "items": [ - { - "aliases": [ - "C#", - "c#" - ], - "languageName": "C#", - "name": "csharp" - }, - { - "aliases": [ - "frontend" - ], - "name": "vscode" - } - ] - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/archive/docs/WMA.md b/archive/docs/WMA.md deleted file mode 100644 index 42792053..00000000 --- a/archive/docs/WMA.md +++ /dev/null @@ -1,49 +0,0 @@ -# WMA: Weighted Moving Average -period = 10 - -![Alt text](./img/WMA_chart.svg) - -WMA is linearly weighted moving Average where the weights are linearly decreasing over the _period_ and the most recent data has the heaviest weight. - -## Calculation - -WMA is a rolling calculation that is looking backwards from the position ${n}$ and is denoted as ${WMA}_{p}{(data)}$ where $p$ represents the period, $w$ represents the assigned weight and $data$ represents the list of data points: -$$ -WMA_p{(data)} = \frac{1}{\sum w }\sum_{i=n-p+1}^{n} w_i data_i -$$ -Weights $w$ are linearly increasing from $1$ to $p$. For example, the weights $w$ for a $p=5$ would be {1, 2, 3, 4, 5} - - - -## Reference Calculation -period = 5 -``` -TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; -WMA_Series wma = new(data, 5, useNaN: false); -WMA_Series wma_nan = new(data, 5, useNaN: true); -for (int i=0; i< data.Count; i++) - Console.WriteLine($"{i}\t{data[i].v,7:f2}\t{wma_nan[i].v,7:f3}\t{wma[i].v,7:f3}"); -``` - -|#|input|wma_NaN|wma| -|--|:--:|:--:|:--:| -|0| 81.59| NaN| 81.590| -|1| 81.06| NaN| 81.237| -|2| 82.87| NaN| 82.053| -|3| 83.00| NaN| 82.432| -|4| 83.61| 82.825| 82.825| -|5| 83.15| 83.066| 83.066| -|6| 82.84| 83.100| 83.100| -|7| 83.99| 83.399| 83.399| -|8| 84.55| 83.809| 83.809| -|9| 84.36| 84.053| 84.053| -|10| 85.53| 84.637| 84.637| -|11| 86.54| 85.399| 85.399| -|12| 86.89| 86.031| 86.031| -|13| 87.77| 86.763| 86.763| -|14| 87.29| 87.121| 87.121| - -## References - - https://en.wikipedia.org/wiki/Moving_average#Weighted_moving_average - - Kaufman, Perry J. (2013) Trading Systems and Methods - - Murphy, J. (1999) Technical Analysis of the Financial Markets \ No newline at end of file diff --git a/archive/docs/ZLEMA.md b/archive/docs/ZLEMA.md deleted file mode 100644 index 5df16918..00000000 --- a/archive/docs/ZLEMA.md +++ /dev/null @@ -1,5 +0,0 @@ -# ZLEMA: Zero-lag Exponential Moving Average - -period = 10 - -![Alt text](./img/ZLEMA_chart.svg) \ No newline at end of file diff --git a/archive/docs/_sidebar.md b/archive/docs/_sidebar.md deleted file mode 100644 index ceb3a715..00000000 --- a/archive/docs/_sidebar.md +++ /dev/null @@ -1,24 +0,0 @@ -* [Home](/) - * [FAQ - Frequently asked questions answered](QA.md) - -* [List of all Indicators](indicators.md "Indicators coverage") - - * [SMA - Simple Moving Average](SMA.md) - * [EMA - Exponential Moving Average](EMA.md) - * [WMA - Weighted Moving Average](WMA.md) - * [T3 - Tillson T3 Exponential MA](T3.md) - * [SMMA - Smoothed Moving Average](SMMA.md) - * [TRIMA - Triangular Moving Average](TRIMA.md) - * [DWMA - Double Weighted Moving Average](DWMA.md) - * [FMA - Fibonacci Moving Average](FMA.md) - * [DEMA - Double Exponential MA](DEMA.md) - * [TEMA - Triple Exponential MA](TEMA.md) - * [ALMA - Arnaud Legoux Moving Average](ALMA.md) - * [HMA - Hull Moving Average](HMA.md) - * [HEMA - Hull/Exponential Moving Average](HEMA.md) - * [HWMA - Holt-Winter Moving Average](HWMA.md) - * [MAMA - MESA Adaptive Moving Average](MAMA.md) - * [KAMA - Kaufman Adaptive Moving Average](KAMA.md) - * [ZLEMA - Zero-Lag Exponential MA](ZLEMA.md) - * [JMA - Jurik Moving Average](JMA.md) - diff --git a/archive/docs/getting_started.ipynb b/archive/docs/getting_started.ipynb deleted file mode 100644 index 681f37e2..00000000 --- a/archive/docs/getting_started.ipynb +++ /dev/null @@ -1,445 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Quick Start\n", - "\n", - "In order to use this .NET Interactive Notebook and play along with QuanTAlib (outside of making your own app or plugging QuanTAlib into Quantower platform), you will need:\n", - "\n", - "- Installed Visual Studio Code\n", - "- Installed .NET 6 SDK\n", - "- Installed .NET Interactive Notebooks extension\n", - "\n", - "**For impatient**, here is a simple example of calculating three moving averages - SMA(data), EMA(SMA(data)) and WMA(EMA(SMA(data))) from 10 days of AAPL stock data using QuanTAlib:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "index\t data\t\t sma(data)\t ema(sma(data))\t wma(ema(sma(data)))\n", - "0\t 2023-03-27\t 158.28\t\t 158.28\t\t NaN\n", - "1\t 2023-03-28\t 157.97\t\t 158.12\t\t NaN\n", - "2\t 2023-03-29\t 158.90\t\t 158.38\t\t NaN\n", - "3\t 2023-03-30\t 159.77\t\t 158.73\t\t NaN\n", - "4\t 2023-03-31\t 160.79\t\t 159.14\t\t 158.69\n", - "5\t 2023-04-03\t 162.37\t\t 160.22\t\t 159.25\n", - "6\t 2023-04-04\t 163.97\t\t 161.47\t\t 160.10\n", - "7\t 2023-04-05\t 164.56\t\t 162.50\t\t 161.07\n", - "8\t 2023-04-06\t 165.02\t\t 163.34\t\t 162.04\n" - ] - } - ], - "source": [ - "#r \"nuget:QuanTAlib;\"\n", - "using QuanTAlib;\n", - "\n", - "Yahoo_Feed aapl = new(\"AAPL\", 10);\n", - "TSeries data = aapl.Close;\n", - "SMA_Series sma = new(source: data, period: 5, useNaN: false);\n", - "EMA_Series ema = new(sma, period: 5); // by default, indicators expose all data, no NaN values\n", - "WMA_Series wma = new(ema, 5, useNaN: true); // for the final calculation we can hide early data with NaNs\n", - "\n", - "Console.Write($\"index\\t data\\t\\t sma(data)\\t ema(sma(data))\\t wma(ema(sma(data)))\\n\");\n", - "for (int i=0; iindexvalue0
(4/7/2023 12:00:00 AM, 105.3)
Item12023-04-07 00:00:00Z
Item2
105.3
1
(4/7/2023 2:34:48 PM, 293.1)
Item12023-04-07 14:34:48Z
Item2
293.1
2
(4/7/2023 2:34:48 PM, 0)
Item12023-04-07 14:34:48Z
Item2
0
3
(4/4/2023 2:34:48 PM, 10)
Item12023-04-04 14:34:48Z
Item2
10
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "var item1 = (DateTime.Today, 105.3); // (DateTime, Value) tuple\n", - "double item2 = 293.1; // a simple double\n", - "\n", - "TSeries data = new();\n", - "data.Add(item1); // adding tuple variable\n", - "data.Add(item2); // QuanTAlib stamps the (double) with current time\n", - "data.Add(0); // directly adding a number (stamped with current time)\n", - "data.Add((DateTime.Now.AddDays(-3), 10)); // adding a tuple with timestamp 3 days ago\n", - "\n", - "data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "TSeries list can display only values (without timestamps) or only timestamps (without values) by using `.v` or `.t` properties" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
[ 105.3, 293.1, 0, 10 ]
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "data.v" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The last element on the list can be accessed by .Last() or by [^1] - and using `.t` (time) and `.v` (value) properties. Also, casting a TSeries into (double) will return the value of the last element" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
10
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "bool IsTheSame = data.Last().v == data[^1].v;\n", - "double lastvalue = data;\n", - "\n", - "lastvalue" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All indicators are just modified TSeries classes; they get all required input during class construction (source of the datafeed, period...) and they automatically subscribe to events of the datafeed. Whenever datafeed gets a new value, indicator will calculate its own value. Indicators are also event publishers, so other indicators can subscribe to their results, chaining indicators together:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
[ Infinity, 0.6666666666666666, 0.3333333333333333, 0.2, 0.14285714285714285, 0.1111111111111111, 0.09090909090909091, 0.07692307692307693, 0.06666666666666667, 0.058823529411764705, 0.25 ]
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "TSeries t1 = new() {0,1,2,3,4,5,6,7,8,9}; // t1 is loaded with data and activated as a publisher\n", - "EMA_Series t2 = new(t1, 3); // t2 will auto-load all history of t1 and wait for events from t1\n", - "ADD_Series t3 = new(t1, t2); // t3 is an ADDition of t1 and t2 - will also load history and wait for t2 events\n", - "DIV_Series t4 = new(1, t3); // t4 is calculating 1/t3 - and waiting for t3 events\n", - "\n", - "TSeries t5 = new(); // a wild indicator appeared! And it is empty!\n", - "t4.Pub += t5.Sub; // let us add a manual subscription to events coming from t4 - t5 is now listening to t4\n", - "t1.Add(0); // we add one new value to t1 - and trigger the full cascade of calculation! t5 is now full!\n", - "\n", - "t5.v" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# MACD compounded indicator\n", - "\n", - "With QuanTAlib we can chain indicators together, creating complex compounded indicators. For example, we can create Moving Average Convergence/Divergence (MACD) indicators by chaining all required operations in a sequence:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.000974358974349343, -0.03456027049873228, -0.13792617985566447, -0.4729486712049916, -0.825402881197467, -0.8902360596814031, -0.9360607784903126, -0.7333381872239422 ... (79 more) ]
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "Yahoo_Feed aapl = new(\"AAPL\", 100);\n", - "TSeries close = aapl.Close; // close will get data from history\n", - "EMA_Series slow = new(close,26); // slow gets data from slow through pub-sub eventing\n", - "EMA_Series fast = new(close,12); // fast gets data from slow (via eventing)\n", - "SUB_Series macd = new(fast,slow); // macd is a SUBtraction: fast-slow\n", - "EMA_Series signal = new(macd,9); // signal is EMA of macd\n", - "SUB_Series histogram = new(macd, signal); // histogram is SUBtraction macd-signal\n", - "\n", - "histogram.v\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".NET (C#)", - "language": "C#", - "name": ".net-csharp" - }, - "language_info": { - "name": "polyglot-notebook" - }, - "polyglot_notebook": { - "kernelInfo": { - "defaultKernelName": "csharp", - "items": [ - { - "aliases": [ - "C#", - "c#" - ], - "languageName": "C#", - "name": "csharp" - }, - { - "aliases": [], - "languageName": "KQL", - "name": "kql" - }, - { - "aliases": [ - "frontend" - ], - "name": "vscode" - } - ] - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/archive/docs/img/ALMA_chart.svg b/archive/docs/img/ALMA_chart.svg deleted file mode 100644 index 2e3ef221..00000000 --- a/archive/docs/img/ALMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/DEMA_chart.svg b/archive/docs/img/DEMA_chart.svg deleted file mode 100644 index 16913aa6..00000000 --- a/archive/docs/img/DEMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.5102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/DWMA_chart.svg b/archive/docs/img/DWMA_chart.svg deleted file mode 100644 index 2ed01660..00000000 --- a/archive/docs/img/DWMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/EMA_chart.svg b/archive/docs/img/EMA_chart.svg deleted file mode 100644 index 7fe95988..00000000 --- a/archive/docs/img/EMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/FMA_chart.svg b/archive/docs/img/FMA_chart.svg deleted file mode 100644 index 93d2997e..00000000 --- a/archive/docs/img/FMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/HEMA_chart.svg b/archive/docs/img/HEMA_chart.svg deleted file mode 100644 index ed35c54f..00000000 --- a/archive/docs/img/HEMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.5102040600102030020406001020300204060−1−0.500.510204060−1010204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.511.50204060−1001020300204060−1010204060−10120204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/HMA_chart.svg b/archive/docs/img/HMA_chart.svg deleted file mode 100644 index 7fa9c1b1..00000000 --- a/archive/docs/img/HMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.51020406000.510204060−100102030020406001020300204060−1−0.500.510204060−1010204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−10120204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/HWMA_chart.svg b/archive/docs/img/HWMA_chart.svg deleted file mode 100644 index 660fa53f..00000000 --- a/archive/docs/img/HWMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.510204060−100102030020406001020300204060−1−0.500.510204060−1010204060−1010204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.511.50204060−100102030400204060−1010204060−10120204060170175 \ No newline at end of file diff --git a/archive/docs/img/JMA_chart.svg b/archive/docs/img/JMA_chart.svg deleted file mode 100644 index 4be3d13e..00000000 --- a/archive/docs/img/JMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/KAMA_chart.svg b/archive/docs/img/KAMA_chart.svg deleted file mode 100644 index 2ed53b3a..00000000 --- a/archive/docs/img/KAMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/MAMA_chart.svg b/archive/docs/img/MAMA_chart.svg deleted file mode 100644 index fd2cef35..00000000 --- a/archive/docs/img/MAMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/RMA_chart.svg b/archive/docs/img/RMA_chart.svg deleted file mode 100644 index 3be85be9..00000000 --- a/archive/docs/img/RMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/SMA_chart.svg b/archive/docs/img/SMA_chart.svg deleted file mode 100644 index 9de5db90..00000000 --- a/archive/docs/img/SMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/SMMA_chart.svg b/archive/docs/img/SMMA_chart.svg deleted file mode 100644 index e856b1f3..00000000 --- a/archive/docs/img/SMMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/T3_chart.svg b/archive/docs/img/T3_chart.svg deleted file mode 100644 index 906a89ab..00000000 --- a/archive/docs/img/T3_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.5102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/TEMA_chart.svg b/archive/docs/img/TEMA_chart.svg deleted file mode 100644 index 4351664f..00000000 --- a/archive/docs/img/TEMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.5102040600102030020406001020300204060−1−0.500.510204060−1010204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.510204060−1001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/TRIMA_chart.svg b/archive/docs/img/TRIMA_chart.svg deleted file mode 100644 index ad79cb56..00000000 --- a/archive/docs/img/TRIMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/WMA_chart.svg b/archive/docs/img/WMA_chart.svg deleted file mode 100644 index 15e010d0..00000000 --- a/archive/docs/img/WMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/ZLEMA_chart.svg b/archive/docs/img/ZLEMA_chart.svg deleted file mode 100644 index fe9ceab2..00000000 --- a/archive/docs/img/ZLEMA_chart.svg +++ /dev/null @@ -1 +0,0 @@ -020406000.20.40.60.81020406000.5102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/archive/docs/img/quotes.gif b/archive/docs/img/quotes.gif deleted file mode 100644 index f7ffdd1b..00000000 Binary files a/archive/docs/img/quotes.gif and /dev/null differ diff --git a/archive/docs/index.html b/archive/docs/index.html deleted file mode 100644 index e6588dd8..00000000 --- a/archive/docs/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Document - - - - - - - -
- - - - - - - - - diff --git a/archive/docs/indicators.md b/archive/docs/indicators.md deleted file mode 100644 index 4c5301fe..00000000 --- a/archive/docs/indicators.md +++ /dev/null @@ -1,175 +0,0 @@ -# Coverage - -⭐= Calculation is validated against several TA libraries - -✔️= Validation tests passed - -❌= Issue - -|**BASIC TRANSFORMS**|**QuanTAlib**|**TA-LIB**|**Skender**|**Pandas TA**|**Tulip**| -|--|:--:|:--:|:--:|:--:|:--:| -|OC2 - (Open+Close)/2|️ `.OC2`||✔️CandlePart.OC2|| -|⭐HL2 - Median Price|`.HL2`|✔️MEDPRICE|✔️CandlePart.HL2|✔️hl2|✔️medprice| -|⭐HLC3 - Typical Price|`.HLC3`|✔️TYPPRICE|✔️CandlePart.HLC3|✔️hlc3|✔️typprice| -|OHL3 - (Open+High+Low)/3|`.OHL3`||✔️CandlePart.OHL3|| -|⭐OHLC4 - Average Price|`.OHLC4`|✔️AVGPRICE|️✔️CandlePart.OHLC4|✔️ohlc4|✔️avgprice| -|HLCC4 - Weighted Price|`.HLCC4`|✔️WCLPRICE|||✔️wcprice| -|MIDPOINT - Midpoint value|`MIDPOINT_Series`|✔️MIDPOINT||midpoint| -|MIDPRICE - Midpoint price|`MIDPRICE_Series`|✔️MIDPRICE||midprice| -|MAX - Max value|`MAX_Series`|✔️MAX|||✔️max| -|MIN - Min value|`MIN_Series`|✔️MIN|||✔️min| -|SUM - Summation|`SUM_Series`|✔️SUM|||✔️sum| -|ADD - Addition|`ADD_Series`|✔️ADD|||✔️add| -|SUB - Subtraction|`SUB_Series`|✔️SUB|||✔️sub| -|MUL - Multiplication|`MUL_Series`|✔️MUL|||✔️mul| -|DIV - Division|`DIV_Series`|✔️DIV|||✔️div| -||||| -|**STATISTICS & NUMERICAL ANALYSIS**| -|||||| -|BIAS - Bias|`BIAS_Series`|||✔️bias| -|CORR - Pearson's Correlation Coefficient|`CORR_Series`|✔️CORREL|✔️GetCorrelation|| -|COVAR - Covariance|`COVAR_Series`||✔️GetCorrelation|| -|DECAY - Linear Decay|`DECAY_Series`|||decay|✔️decay| -|EDECAY - Exponential Decay|`DECAY_Series`|||decay|✔️edecay| -|ENTROPY - Entropy|`ENTROPY_Series`|||entropy|| -|KURTOSIS - Kurtosis|`KURT_Series`|||✔️kurtosis| -|SLOPE - Slope of Linear Regression|`SLOPE_Series`||✔️GetSlope||✔️linregslope| -|MAD - Mean Absolute Deviation|`MAD_Series`||✔️GetSmaAnalysis|✔️mad| -|MAE - Mean Absolute Error|`MAE_Series`|||| -|MAPE - Mean Absolute Percent Error|`MAPE_Series`||✔️GetSmaAnalysis|| -|MEDIAN - Median value|`MEDIAN_Series`|||✔️median| -|MSE - Mean Squared Error|`MSE_Series`||✔️GetSmaAnalysis|| -|SKEW - Skewness||||skew| -|⭐SDEV - Standard Deviation (Volatility)|`SDEV_Series`|✔️STDDEV|✔️GetStdDev|✔️stdev|✔️stddev| -|SSDEV - Sample Standard Deviation|`SSDEV_Series`|||✔️stdev| -|SMAPE - Symmetric Mean Absolute Percent Error|`SMAPE_Series`|||| -|VAR - Population Variance|`VAR_Series`|✔️VAR||✔️variance|✔️var| -|SVAR - Sample Variance|`SVAR_Series`|||✔️variance| -|QUANTILE - Quantile||||quantile| -|WMAPE - Weighted Mean Absolute Percent Error|`WMAPE_Series`|||| -|ZSCORE - Number of standard deviations from mean|`ZSCORE_Series`||✔️GetStdDev|✔️zscore| -|||||| -|**TREND INDICATORS & AVERAGES**| -|||||| -|AFIRMA - Autoregressive Finite Impulse Response Moving Average||||| -|ALMA - Arnaud Legoux Moving Average|`ALMA_Series`||✔️GetAlma|alma| -|DEMA - Double EMA Average|`DEMA_Series`|✔️DEMA|✔️GetDema|✔️dema|✔️dema| -|DWMA - Double WMA Average|`DWMA_Series`||||| -|⭐[EMA - Exponential Moving Average](EMA.md)|`EMA_Series`|✔️EMA|✔️GetEma|✔️ema|✔️ema| -|EPMA - Endpoint Moving Average|||GetEpma|| -|FRAMA - Fractal Adaptive Moving Average||||| -|FMA - Fibonacci's Weighted Moving Average|`FMA_Series`|||fwma| -|HILO - Gann High-Low Activator||||hilo| -|HEMA - Hull/EMA Average|`HEMA_Series`|||| -|Hilbert Transform Instantaneous Trendline||HT_TRENDLINE|GetHtTrendline|| -|⭐HMA - Hull Moving Average|`HMA_Series`||✔️GetHma|✔️hma|✔️hma| -|HWMA - Holt-Winter Moving Average|`HWMA_Series`|||✔️hwma| -|JMA - Jurik Moving Average|`JMA_Series`|||jma|| -|KAMA - Kaufman's Adaptive Moving Average|`KAMA_Series`|✔️KAMA|✔️GetKama|✔️kama|✔️kama| -|KDJ - KDJ Indicator (trend reversal)||||kdj| -|LSMA - Least Squares Moving Average|||GetEpma|| -|⭐MACD - Moving Average Convergence/Divergence|`MACD_Series`|✔️MACD|✔️GetMacd|✔️macd|✔️macd| -|MAMA - MESA Adaptive Moving Average|`MAMA_Series`|✔️MAMA|✔️GetMama|| -|MCGD - McGinley Dynamic||||mcgd| -|MMA - Modified Moving Average||||| -|PPMA - Pivot Point Moving Average||||| -|PWMA - Pascal's Weighted Moving Average||||pwma| -|⭐RMA - WildeR's Moving Average|`RMA_Series`|||✔️rma|✔️rma| -|SINWMA - Sine Weighted Moving Average||||sinwma| -|⭐[SMA - Simple Moving Average](SMA.md)|`SMA_Series`|✔️SMA|✔️GetSma|✔️sma|✔️sma| -|SMMA - Smoothed Moving Average|`SMMA_Series`||✔️GetSmma|| -|SSF - Ehler's Super Smoother Filter||||ssf| -|SUPERTREND - Supertrend||||supertrend| -|SWMA - Symmetric Weighted Moving Average||||swma| -|T3 - Tillson T3 Moving Average|`T3_Series`|✔️T3|✔️GetT3|✔️t3|| -|⭐TEMA - Triple EMA Average|`TEMA_Series`|✔️TEMA|✔️GetTema|✔️tema|✔️tema| -|⭐TRIMA - Triangular Moving Average|`TRIMA_Series`|✔️TRIMA||✔️trima|✔️trima| -|TSF - Time Series Forecast||TSF||| -|VIDYA - Variable Index Dynamic Average||||vidya|vidya| -|VORTEX - Vortex Indicator||||vortex| -|⭐WMA - Weighted Moving Average|`WMA_Series`|✔️WMA|✔️GetWma|✔️wma|✔️wma| -|ZLEMA - Zero Lag EMA Average|`ZLEMA_Series`|||✔️zlma|❌zlema| -|||||| -|**VOLATILITY INDICATORS**| -|||||| -|⭐ADL - Chaikin Accumulation Distribution Line|`ADL_Series`|✔️AD|✔️GetAdl|✔️ad|✔️ad| -|⭐ADOSC - Chaikin Accumulation Distribution Oscillator|`ADOSC_Series`|✔️ADOSC||✔️adosc|✔️adosc| -|⭐ATR - Average True Range|`ATR_Series`|✔️ATR|✔️GetAtr|✔️atr|✔️atr| -|ATRP - Average True Range Percent|`ATRP_Series`||✔️GetAtr|| -|BETA - Beta coefficient||BETA|GetBeta|| -|⭐BBANDS - Bollinger Bands®|`BBANDS_Series`|✔️BBANDS|✔️GetBollingerBands|✔️bbands|✔️bbands| -|CHAND - Chandelier Exit|||GetChandelier|| -|CRSI - Connor RSI|||GetConnorsRsi|| -|CVI - Chaikins Volatility|||||cvi| -|DON - Donchian Channels|||GetDonchian|| -|FCB - Fractal Chaos Bands|||GetFcb|| -|FISHER - Fisher Transform|||GetFcb||fisher| -|HV - Historical Volatility||||| -|ICH - Ichimoku|||GetIchimoku|| -|KEL - Keltner Channels|||GetKeltner|| -|NATR - Normalized Average True Range||NATR|GetAtr|| -|CHN - Price Channel Indicator||||| -|RSI - Relative Strength Index|`RSI_Series`|✔️RSI|✔️GetRsi|✔️rsi|✔️rsi| -|SAR - Parabolic Stop and Reverse||SAR|GetParabolicSar|| -|SRSI - Stochastic RSI||STOCHRSI|GetStochRsi|| -|STARC - Starc Bands||||| -|TR - True Range|`TR_Series`|✔️TRANGE|✔️GetTr|✔️true_range|✔️tr| -|UI - Ulcer Index||||| -|VSTOP - Volatility Stop||||| -|||||| -|**MOMENTUM INDICATORS & OSCILLATORS**| -|||||| -|AC - Acceleration Oscillator||||| -|ADX - Average Directional Movement Index||ADX|GetAdx||adx| -|ADXR - Average Directional Movement Index Rating||ADXR|GetAdx||adxr| -|AO - Awesome Oscillator|||GetAwesome||ao| -|APO - Absolute Price Oscillator||APO|||apo| -|AROON - Aroon oscillator||AROON|GetAroon||aroon| -|BOP - Balance of Power||BOP|GetBop||bop| -|CCI - Commodity Channel Index|`CCI_Series`|✔️CCI|✔️GetCci||❌cci| -|CFO - Chande Forcast Oscillator||||| -|CMO - Chande Momentum Oscillator|`CMO_Series`|❌CMO|✔️GetCmo|❌cmo|✔️cmo| -|COG - Center of Gravity||||| -|COPPOCK - Coppock Curve||||| -|CTI - Ehler's Correlation Trend Indicator||||| -|DPO - Detrended Price Oscillator|||GetDpo|| -|DMI - Directional Movement Index||DX|GetAdx|| -|EFI - Elder Ray's Force Index|||GetElderRay|| -|FOSC - Forecast oscillator|||||fosc| -|GAT - Alligator oscillator|||GetGator|| -|HURST - Hurst Exponent|||GetHurst|| -|KRI - Kairi Relative Index||||| -|KVO - Klinger Volume Oscillator|||||| -|MFI - Money Flow Index||MFI|GetMfi|| -|MOM - Momentum||MOM||| -|NVI - Negative Volume Index||||| -|PO - Price Oscillator||||| -|PPO - Percentage Price Oscillator||PPO||| -|PMO - Price Momentum Oscillator||||| -|PVI - Positive Volume Index||||| -|ROC - Rate of Change||MOM|GetRoc|| -|RVGI - Relative Vigor Index||||| -|SMI - Stochastic Momentum Index||||| -|STC - Schaff Trend Cycle||||| -|STOCH - Stochastic Oscillator||STOCH|GetStoch|| -|TRIX - 1-day ROC of TEMA|`TRIX_Series`|✔️TRIX|✔️GetTrix|✔️trix|❌trix| -|TSI - True Strength Index||||| -|UO - Ultimate Oscillator||ULTOSC|GetUltimate||ultosc| -|WILLR - Larry Williams' %R||WILLR|GetWilliamsR||willr| -|WGAT - Williams Alligator||||| -|||||| -|**VOLUME INDICATORS**| -|||||| -|AOBV - Archer On-Balance Volume||||| -|CMF - Chaikin Money Flow||||| -|EOM - Ease of Movement|||||emv| -|KVO - Klinger Volume Oscilaltor|||||kvo| -|OBV - On-Balance Volume|`OBV_Series`|✔️OBV|✔️GetObv|✔️obv|❌obv| -|PRS - Price Relative Strength|||| -|PVOL - Price-Volume||||| -|PVO - Percentage Volume Oscillator||||| -|PVR - Price Volume Rank||||| -|PVT - Price Volume Trend||||| -|VP - Volume Profile||||| -|VWAP - Volume Weighted Average Price||||| -|VWMA - Volume Weighted Moving Average|||||vwma| diff --git a/archive/docs/readme.md b/archive/docs/readme.md deleted file mode 100644 index a8c3d92d..00000000 --- a/archive/docs/readme.md +++ /dev/null @@ -1,44 +0,0 @@ -# QuanTAlib - quantitative technical indicators for Quantower and other C#-based trading platorms - -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=mihakralj_QuanTAlib&metric=ncloc)](https://sonarcloud.io/summary/overall?id=mihakralj_QuanTAlib) -[![Codacy grade](https://img.shields.io/codacy/grade/b1f9109222234c87bce45f1fd4c63aee?style=flat-square)](https://app.codacy.com/gh/mihakralj/QuanTAlib/dashboard) -[![codecov](https://codecov.io/gh/mihakralj/QuanTAlib/branch/main/graph/badge.svg?style=flat-square&token=YNMJRGKMTJ?style=flat-square)](https://codecov.io/gh/mihakralj/QuanTAlib) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=mihakralj_QuanTAlib&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=mihakralj_QuanTAlib) -[![CodeFactor](https://www.codefactor.io/repository/github/mihakralj/quantalib/badge/main)](https://www.codefactor.io/repository/github/mihakralj/quantalib/overview/main) - -[![Nuget](https://img.shields.io/nuget/v/QuanTAlib?style=flat-square)](https://www.nuget.org/packages/QuanTAlib/) -![GitHub last commit](https://img.shields.io/github/last-commit/mihakralj/QuanTAlib) -[![Nuget](https://img.shields.io/nuget/dt/QuanTAlib?style=flat-square)](https://www.nuget.org/packages/QuanTAlib/) -[![GitHub watchers](https://img.shields.io/github/watchers/mihakralj/QuanTAlib?style=flat-square)](https://github.com/mihakralj/QuanTAlib/watchers) -[![.NET7.0](https://img.shields.io/badge/.NET-7.0%20%7C%206.0%20%7C%204.8-blue?style=flat-square)](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) - -**Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for analyzing quotes with [Quantower](https://www.quantower.com/) and other C#-based trading platforms. - -**QuanTAlib** is written with some specific design criteria in mind - why there is '_yet another C# TA library_': - -- Prioritize **real-time data analysis** (series can add new data and indicator doesn't have to re-calculate the whole history) -- **Allow updates** to the last quote and adjusting the calculation to the still-forming bar -- **Calculate early data right** - output data is as valid as mathematically possible from the first value onwards - -![Alt text](./img/quotes.gif) - -If not obvious, QuanTAlib is intended for developers, and it does not focus on sources of OHLCV quotes. There are some very basic data feeds available to use in the learning process: `RND_Feed` and `GBM_Feed` for random data, `Yahoo_Feed` and `Alphavantage_Feed` for a quick grab of daily data of US stock market. - -See [Getting Started](https://github.com/mihakralj/QuanTAlib/blob/main/Docs/getting_started.ipynb) .NET interactive notebook to get a feel how library works. Developers can use QuanTAlib in [Polyglot Notebooks](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) or in console apps, but the best usage of the library is with C#-enabled trading platforms - see **QuanTower_Charts** folder for Quantower examples and check **Releases** for compiled Quantower DLL. - -### Coverage - -[List of all indicators - current and planned](indicators.md) - -### Validation - -QuanTAlib uses validation tests with four other TA libraries to assure accuracy and validity of results: - -- [TA-LIB](https://www.ta-lib.org/function.html) -- [Skender Stock Indicators](https://dotnet.stockindicators.dev/) -- [Pandas-TA](https://twopirllc.github.io/pandas-ta/) -- [Tulip Indicators](https://tulipindicators.org/) - -### Questions - -[Some most common questions addressed](QA.md) \ No newline at end of file diff --git a/benchmark/benchmark.csproj b/benchmark/benchmark.csproj new file mode 100644 index 00000000..0940953e --- /dev/null +++ b/benchmark/benchmark.csproj @@ -0,0 +1,28 @@ + + + + Exe + net8.0 + false + enable + latest + false + + + + + + + + + + + + true + AnyCPU + pdbonly + true + true + + + \ No newline at end of file diff --git a/benchmark/growthBench.cs b/benchmark/growthBench.cs new file mode 100644 index 00000000..6dd123e4 --- /dev/null +++ b/benchmark/growthBench.cs @@ -0,0 +1,73 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Running; + +namespace QuanTAlib; + +public class Program +{ + public static void Main(string[] args) + { + var config = DefaultConfig.Instance + .WithOption(ConfigOptions.DisableOptimizationsValidator, true); + BenchmarkRunner.Run(config); + } +} + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 3, iterationCount: 5)] +public class EmaBenchmark +{ + private const int Period = 10; + private const int Length = 100_000; + private GbmFeed gbm = null!; + private TSeries inputs = null!; + + [GlobalSetup] + public void Setup() + { + gbm = new GbmFeed(); + inputs = new(); + + for (int i = 0; i < Length; i++) + { + TBar item = gbm.Generate(DateTime.Now); + inputs.Add(new TValue(item.Time, item.Close, true, true)); + } + } + + [Benchmark] + public void Ema_bench() + { + Ema ma1 = new(Period); + for (int i = 0; i < Length; i++) + { + TValue item = gbm.Generate(DateTime.Now).Close; + ma1.Calc(item); + } + + } + + [Benchmark] + public void Alma_bench() + { + Alma ma1 = new(Period); + for (int i = 0; i < Length; i++) + { + TValue item = gbm.Generate(DateTime.Now).Close; + ma1.Calc(item); + } + } + + [Benchmark] + public void Dema_bench() + { + Dema ma1 = new(Period); + for (int i = 0; i < Length; i++) + { + TValue item = gbm.Generate(DateTime.Now).Close; + ma1.Calc(item); + } + } +} \ No newline at end of file diff --git a/docs/Progress.csv b/docs/Progress.csv new file mode 100644 index 00000000..7ea5a50e --- /dev/null +++ b/docs/Progress.csv @@ -0,0 +1,134 @@ +AC,Acceleration Oscillator +AD,Chaikin A/D Line +ADOSC,Chaikin A/D Oscillator +ADL,Accumulation / Distribution Line +ADX,Average Directional Movement Index +ADXR,Average Directional Movement Index Rating +Alligator,Alligator Indicator +ALMA,Arnaud Legoux Moving Average +AO,Awesome Oscillator +APO,Absolute Price Oscillator +AROON,Aroon Indicator +AROONOSC,Aroon Oscillator +ATS,ATR Trailing Stop +ATR,Average True Range +AVGPRICE,Average Price +BB,Bollinger Bands +BBF,Bollinger Bands Flat +BBP,Bull and Bear Power +BC,Beta Coefficient +BETA,Beta +BOP,Balance of Power +CCI,Commodity Channel Index +CE,Chandelier Exit +Channel,Price Channel +CI,Choppiness Index +CMF,Chaikin Money Flow +CMO,Chande Momentum Oscillator +CORREL,Pearson's Correlation Coefficient +CRSI,ConnorsRSI +DC,Donchian Channels +DCP,Dominant Cycle Periods +DEMA,Double Exponential Moving Average +DMI,Directional Movement Index +DPO,Detrended Price Oscillator +DX,Directional Movement Index +EMA,Exponential Moving Average +EPMA,Endpoint Moving Average +ERI,Elder-ray Index +FCB,Fractal Chaos Bands +FI,Force Index +GO,Gator Oscillator +HE,Hurst Exponent +HMA,Hull Moving Average +HT_TRENDLINE,Hilbert Transform - Instantaneous Trendline +HT_TRENDMODE,Hilbert Transform - Trend vs Cycle Mode +HV,Historical Volatility +IC,Ichimoku Cloud +KAMA,Kaufman Adaptive Moving Average +KDJ,KDJ Index +Keltner,Keltner Channel +KVO,Klinger Volume Oscillator +LINEARREG,Linear Regression +LINEARREG_ANGLE,Linear Regression Angle +LINEARREG_INTERCEPT,Linear Regression Intercept +LINEARREG_SLOPE,Linear Regression Slope +LSMA,Least Squares Moving Average +LWMA,Linearly Weighted Moving Average +MACDEXT,MACD with controllable MA type +MACDFIX,Moving Average Convergence Divergence Fix 12/26 +MAD,Mean absolute deviation +MAE,Moving Average Envelope +MAMA,MESA Adaptive Moving Average +MAPE,Mean absolute percentage error +MAX,Highest value over a specified period +MAXINDEX,Index of highest value over a specified period +MD,McGinley Dynamic +MEDPRICE,Median Price +MFI,Money Flow Index +MIN,Lowest value over a specified period +MININDEX,Index of lowest value over a specified period +MINMAX,Lowest and highest values over a specified period +MINMAXINDEX,Indexes of lowest and highest values over a period +MMA,Modified Moving Average +MOM,Momentum +MSE,Mean square error +NATR,Normalized Average True Range +OBV,On Balance Volume +OsMA,Moving Average of Oscillator +PAZ,Price Action Zones +Pivots,Pivots +PMO,Price Momentum Oscillator +PP,Pivot Points +PPO,Percentage Price Oscillator +PPMA,Pivot Point Moving Average +PRS,Price Relative Strength +PVI,Positive Volume Index +PVO,Percentage Volume Oscillator +Qstick,Qstick Indicator +R2,R-Squared (Coefficient of Determination) +Regression,Regression Line Indicator +RLW,%R Larry Williams +RMA,Running Moving Average +ROC,Rate of Change +ROCB,ROC with Bands +RPP,Rolling Pivot Points +RRA,Rescaled Range Analysis +RSI,Relative Strength Index +SAR,Parabolic SAR +SAREXT,Parabolic SAR - Extended +SDC,Standard Deviation Channels +SI,Swing Index +SLR,Slope and Linear Regression +SMA,Simple Moving Average +SMI,Stochastic Momentum Index +SMMA,Smoothed Moving Average +ST,SuperTrend +STARC,STARC Bands +STC,Schaff Trend Cycle +STDDEV,Standard Deviation +STOCH,Stochastic +STOCHF,Stochastic Fast +STOCHRSI,Stochastic Relative Strength Index +SUM,Summation +T3,Triple Exponential Moving Average (T3) +TEMA,Triple Exponential Moving Average +TRANGE,True Range +TRIMA,Triangular Moving Average +TRIX,1-day Rate-Of-Change (ROC) of a Triple Smooth EMA +TSI,True Strength Index +TYPPRICE,Typical Price +UI,Ulcer Index +ULTOSC,Ultimate Oscillator +VAR,Variance +VI,Vortex Indicator +Volume,Volume Indicator +VS,Volatility Stop +VWAP,Volume Weighted Average Price +VWMA,Volume Weighted Moving Average +WA,Williams Alligator +WCLPRICE,Weighted Close Price +WF,Williams Fractal +WMA,Weighted Moving Average +ZS,Z-Score +ZZ,ZigZag Indicator diff --git a/docs/Progress.md b/docs/Progress.md new file mode 100644 index 00000000..eaf705a9 --- /dev/null +++ b/docs/Progress.md @@ -0,0 +1,313 @@ +| AD | Chaikin A/D Line | +| AROON | Aroon Indicator | +| ADX | Average Directional Movement Index | +| ADXR | Average Directional Movement Index Rating | +| DX | Directional Movement Index | +| SAR | Parabolic SAR | +| SAREXT | Parabolic SAR - Extended | +| HT_TRENDLINE | Hilbert Transform - Instantaneous Trendline | +| HT_TRENDMODE | Hilbert Transform - Trend vs Cycle Mode | +| ZZ | ZigZag Indicator | +| DMI | Directional Movement Index | +| Alligator | Alligator Indicator | +| Regression | Regression Line Indicator | +| SI | Swing Index | +| ATS | ATR Trailing Stop | +| ERI | Elder-ray Index | +| GO | Gator Oscillator | +| HE | Hurst Exponent | +| IC | Ichimoku Cloud | +| ST | SuperTrend | +| VI | Vortex Indicator | +| WA | Williams Alligator | +| RSI | Relative Strength Index | +| CCI | Commodity Channel Index | +| MOM | Momentum | +| ROC | Rate of Change | +| PPO | Percentage Price Oscillator | +| AO | Awesome Oscillator | +| CMO | Chande Momentum Oscillator | +| TRIX | 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA | +| ULTOSC | Ultimate Oscillator | +| AROONOSC | Aroon Oscillator | +| ADOSC | Chaikin A/D Oscillator | +| APO | Absolute Price Oscillator | +| STOCH | Stochastic | +| STOCHF | Stochastic Fast | +| STOCHRSI | Stochastic Relative Strength Index | +| Qstick | Qstick Indicator | +| RLW | %R Larry Williams | +| AC | Acceleration Oscillator | +| TSI | True Strength Index | +| CRSI | ConnorsRSI | +| DPO | Detrended Price Oscillator | +| KDJ | KDJ Index | +| STC | Schaff Trend Cycle | +| SMI | Stochastic Momentum Index | +| BB | Bollinger Bands | +| Keltner | Keltner Channel | +| BBF | Bollinger Bands Flat | +| Channel | Price Channel | +| MAE | Moving Average Envelope | +| PAZ | Price Action Zones | +| DC | Donchian Channels | +| FCB | Fractal Chaos Bands | +| PP | Pivot Points | +| RPP | Rolling Pivot Points | +| STARC | STARC Bands | +| SDC | Standard Deviation Channels | +| OBV | On Balance Volume | +| PVI | Positive Volume Index | +| Volume | Volume Indicator | +| MFI | Money Flow Index | +| ADL | Accumulation / Distribution Line | +| CMF | Chaikin Money Flow | +| FI | Force Index | +| KVO | Klinger Volume Oscillator | +| PVO | Percentage Volume Oscillator | +| ATS | ATR Trailing Stop | +| CE | Chandelier Exit | +| SAR | Parabolic SAR | +| ST | SuperTrend | +| VS | Volatility Stop | +| Pivots | Pivots | +| WF | Williams Fractal | +| EMA | Exponential Moving Average | +| SMA | Simple Moving Average | +| LWMA | Linearly Weighted Moving Average | +| SMMA | Smoothed Moving Average | +| MMA | Modified Moving Average | +| KAMA | Kaufman Adaptive Moving Average | +| DEMA | Double Exponential Moving Average | +| TEMA | Triple Exponential Moving Average | +| MAMA | MESA Adaptive Moving Average | +| TRIMA | Triangular Moving Average | +| T3 | Triple Exponential Moving Average (T3) | +| PPMA | Pivot Point Moving Average | +| WMA | Weighted Moving Average | +| ALMA | Arnaud Legoux Moving Average | +| EPMA | Endpoint Moving Average | +| HMA | Hull Moving Average | +| LSMA | Least Squares Moving Average | +| MD | McGinley Dynamic | +| RMA | Running Moving Average | +| VWAP | Volume Weighted Average Price | +| VWMA | Volume Weighted Moving Average | +| ATR | Average True Range | +| NATR | Normalized Average True Range | +| TRANGE | True Range | +| STDDEV | Standard Deviation | +| HV | Historical Volatility | +| BOP | Balance of Power | +| BBP | Bull and Bear Power | +| CI | Choppiness Index | +| DCP | Dominant Cycle Periods | +| PMO | Price Momentum Oscillator | +| PRS | Price Relative Strength | +| ROCB | ROC with Bands | +| RRA | Rescaled Range Analysis | +| UI | Ulcer Index | +| CORREL | Pearson's Correlation Coefficient | +| BETA | Beta | +| VAR | Variance | +| AVGPRICE | Average Price | +| MEDPRICE | Median Price | +| TYPPRICE | Typical Price | +| WCLPRICE | Weighted Close Price | +| SUM | Summation | +| MAX | Highest value over a specified period | +| MIN | Lowest value over a specified period | +| MAXINDEX | Index of highest value over a specified period | +| MININDEX | Index of lowest value over a specified period | +| MINMAX | Lowest and highest values over a specified period | +| MINMAXINDEX | Indexes of lowest and highest values over a period | +| BC | Beta Coefficient | +| MAD | Mean absolute deviation | +| MAPE | Mean absolute percentage error | +| MSE | Mean square error | +| R2 | R-Squared (Coefficient of Determination) | +| SLR | Slope and Linear Regression | +| ZS | Z-Score | +| AD | Chaikin A/D Line | +| AROON | Aroon Indicator | +| ADX | Average Directional Movement Index | +| ADXR | Average Directional Movement Index Rating | +| DX | Directional Movement Index | +| SAR | Parabolic SAR | +| SAREXT | Parabolic SAR - Extended | +| HT_TRENDLINE | Hilbert Transform - Instantaneous Trendline | +| HT_TRENDMODE | Hilbert Transform - Trend vs Cycle Mode | +| ZZ | ZigZag Indicator | +| DMI | Directional Movement Index | +| Alligator | Alligator Indicator | +| Regression | Regression Line Indicator | +| SI | Swing Index | +| ATS | ATR Trailing Stop | +| ERI | Elder-ray Index | +| GO | Gator Oscillator | +| HE | Hurst Exponent | +| IC | Ichimoku Cloud | +| ST | SuperTrend | +| VI | Vortex Indicator | +| WA | Williams Alligator | +| RSI | Relative Strength Index | +| CCI | Commodity Channel Index | +| MOM | Momentum | +| ROC | Rate of Change | +| PPO | Percentage Price Oscillator | +| AO | Awesome Oscillator | +| CMO | Chande Momentum Oscillator | +| TRIX | 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA | +| ULTOSC | Ultimate Oscillator | +| AROONOSC | Aroon Oscillator | +| ADOSC | Chaikin A/D Oscillator | +| APO | Absolute Price Oscillator | +| STOCH | Stochastic | +| STOCHF | Stochastic Fast | +| STOCHRSI | Stochastic Relative Strength Index | +| Qstick | Qstick Indicator | +| RLW | %R Larry Williams | +| AC | Acceleration Oscillator | +| TSI | True Strength Index | +| CRSI | ConnorsRSI | +| DPO | Detrended Price Oscillator | +| KDJ | KDJ Index | +| STC | Schaff Trend Cycle | +| SMI | Stochastic Momentum Index | +| BB | Bollinger Bands | +| Keltner | Keltner Channel | +| BBF | Bollinger Bands Flat | +| Channel | Price Channel | +| MAE | Moving Average Envelope | +| PAZ | Price Action Zones | +| DC | Donchian Channels | +| FCB | Fractal Chaos Bands | +| PP | Pivot Points | +| RPP | Rolling Pivot Points | +| STARC | STARC Bands | +| SDC | Standard Deviation Channels | +| OBV | On Balance Volume | +| PVI | Positive Volume Index | +| Volume | Volume Indicator | +| MFI | Money Flow Index | +| ADL | Accumulation / Distribution Line | +| CMF | Chaikin Money Flow | +| FI | Force Index | +| KVO | Klinger Volume Oscillator | +| PVO | Percentage Volume Oscillator | +| ATS | ATR Trailing Stop | +| CE | Chandelier Exit | +| SAR | Parabolic SAR | +| ST | SuperTrend | +| VS | Volatility Stop | +| Pivots | Pivots | +| WF | Williams Fractal | +| ALMA | Arnaud Legoux Moving Average | +| EPMA | Endpoint Moving Average | +| HMA | Hull Moving Average | +| LSMA | Least Squares Moving Average | +| MD | McGinley Dynamic | +| RMA | Running Moving Average | +| T3 | Tillson T3 Moving Average | +| VWAP | Volume Weighted Average Price | +| VWMA | Volume Weighted Moving Average | +| BOP | Balance of Power | +| BBP | Bull and Bear Power | +| CI | Choppiness Index | +| DCP | Dominant Cycle Periods | +| PMO | Price Momentum Oscillator | +| PRS | Price Relative Strength | +| ROCB | ROC with Bands | +| RRA | Rescaled Range Analysis | +| UI | Ulcer Index | +| BC | Beta Coefficient | +| MAD | Mean absolute deviation | +| MAPE | Mean absolute percentage error | +| MSE | Mean square error | +| R2 | R-Squared (Coefficient of Determination) | +| SLR | Slope and Linear Regression | +| ZS | Z-Score | +| EMA | Exponential Moving Average | +| SMA | Simple Moving Average | +| LWMA | Linearly Weighted Moving Average | +| SMMA | Smoothed Moving Average | +| MMA | Modified Moving Average | +| KAMA | Kaufman Adaptive Moving Average | +| DEMA | Double Exponential Moving Average | +| TEMA | Triple Exponential Moving Average | +| MAMA | MESA Adaptive Moving Average | +| TRIMA | Triangular Moving Average | +| T3 | Triple Exponential Moving Average (T3) | +| PPMA | Pivot Point Moving Average | +| MAE | Moving Average Envelope | +| MACD | Moving Average Convergence Divergence | +| MACDEXT | MACD with controllable MA type | +| MACDFIX | Moving Average Convergence Divergence Fix 12/26 | +| OsMA | Moving Average of Oscillator | +| Regression | Regression Indicator | +| LINEARREG | Linear Regression | +| LINEARREG_ANGLE | Linear Regression Angle | +| LINEARREG_INTERCEPT | Linear Regression Intercept | +| LINEARREG_SLOPE | Linear Regression Slope | +| RSI | Relative Strength Index | +| CCI | Commodity Channel Index | +| MOM | Momentum | +| ROC | Rate of Change | +| PPO | Percentage Price Oscillator | +| AO | Awesome Oscillator | +| CMO | Chande Momentum Oscillator | +| TRIX | 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA | +| ULTOSC | Ultimate Oscillator | +| AROONOSC | Aroon Oscillator | +| ADOSC | Chaikin A/D Oscillator | +| APO | Absolute Price Oscillator | +| STOCH | Stochastic | +| STOCHF | Stochastic Fast | +| STOCHRSI | Stochastic Relative Strength Index | +| Qstick | Qstick Indicator | +| RLW | %R Larry Williams | +| AC | Acceleration Oscillator | +| TSI | True Strength Index | +| AD | Chaikin A/D Line | +| AROON | Aroon Indicator | +| ADX | Average Directional Movement Index | +| ADXR | Average Directional Movement Index Rating | +| DX | Directional Movement Index | +| SAR | Parabolic SAR | +| SAREXT | Parabolic SAR - Extended | +| HT_TRENDLINE | Hilbert Transform - Instantaneous Trendline | +| HT_TRENDMODE | Hilbert Transform - Trend vs Cycle Mode | +| ZZ | ZigZag Indicator | +| DMI | Directional Movement Index | +| Alligator | Alligator Indicator | +| Regression | Regression Line Indicator | +| SI | Swing Index | +| ATR | Average True Range | +| NATR | Normalized Average True Range | +| TRANGE | True Range | +| STDDEV | Standard Deviation | +| HV | Historical Volatility | +| BB | Bollinger Bands | +| Keltner | Keltner Channel | +| BBF | Bollinger Bands Flat | +| Channel | Price Channel | +| MAE | Moving Average Envelope | +| PAZ | Price Action Zones | +| OBV | On Balance Volume | +| PVI | Positive Volume Index | +| Volume | Volume Indicator | +| MFI | Money Flow Index | +| CORREL | Pearson's Correlation Coefficient | +| BETA | Beta | +| VAR | Variance | +| AVGPRICE | Average Price | +| MEDPRICE | Median Price | +| TYPPRICE | Typical Price | +| WCLPRICE | Weighted Close Price | +| SUM | Summation | +| MAX | Highest value over a specified period | +| MIN | Lowest value over a specified period | +| MAXINDEX | Index of highest value over a specified period | +| MININDEX | Index of lowest value over a specified period | +| MINMAX | Lowest and highest values over a specified period | +| MINMAXINDEX | Indexes of lowest and highest values over a period | diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 2bdd1409..5df2cf71 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,20 +1,62 @@ -* [QuanTAlib](/) -* [Indicators](indicators/indicators.md) - * Averages & Trends + +* [Home](/) +* Introduction + * [Overview]() + * [Features]() + * [Historical vs Real-time analysis](essays/realtime.md) + +* Core Concepts + * [Time Series Data Handling]() + * [Calculation classes]() + * [Presentation Classes]() + +* QuanTAlib C# Library + * [Installation]() + * [Quick Start Guide]() + * [Usage Examples]() + * [Tests and Validation]() + +* Quantower Charts + * [Installation]() + * [Quick Start Guide]() + * [Using VS Code for QuanTower coding](setup/vscode.md) + * [Using DotPeek](setup/dotpeek.md) + * [Creating Custom Indicators]() + * [Inspecting Quantower Internals]() + +* [Available Indicators](indicators/indicators.md) + * Basic Transforms + * Numerical Analysis + * Errors + * Moving Averages * [AFIRMA - Adaptive Filtering Integrated Recursive Moving Average](indicators/averages/afirma/afirma.md) - * [AFIRMA Charts](indicators/averages/afirma/charts.md) + * [Calculation](indicators/averages/afirma/calc.md) + * [Analysis](indicators/averages/afirma/analysis.md) + * [Charts](indicators/averages/afirma/charts.md) * [ALMA - Arnaud Legoux Moving Average](indicators/averages/alma/alma.md) - * [ALMA Charts](indicators/averages/alma/charts.md) + * [Calculation](indicators/averages/alma/calc.md) + * [Analysis](indicators/averages/alma/analysis.md) + * [Charts](indicators/averages/alma/charts.md) + * [AMA - Adaptive Moving Average](indicators/averages/ama/ama.md) + * [Calculation](indicators/averages/ama/calc.md) + * [Analysis](indicators/averages/ama/analysis.md) + * [Charts](indicators/averages/ama/charts.md) * [DEMA - Double Exponential Moving Average](indicators/averages/dema/dema.md) - * [DEMA Charts](indicators/averages/dema/charts.md) + * [Calculation](indicators/averages/dema/calc.md) + * [Analysis](indicators/averages/dema/analysis.md) + * [Charts](indicators/averages/dema/charts.md) * [DSMA - Deviation Scaled Moving Average](indicators/averages/dsma/dsma.md) - * [DSMA Charts](indicators/averages/dsma/charts.md) - * DWMA - Double Weighted Moving Average - * [DWMA Charts](indicators/averages/dwma/charts.md) + * [Calculation](indicators/averages/dsma/calculation.md) + * [Quality](indicators/averages/dsma/quality.md) + * [Charts](indicators/averages/dsma/charts.md) + * [DWMA - Double Weighted Moving Average](indicators/averages/dwma/calculation.md) + * [Calculation](indicators/averages/dwma/calculation.md) + * [Quality](indicators/averages/dwma/quality.md) + * [Charts](indicators/averages/dwma/charts.md) * [EMA - Exponential Moving Average](indicators/averages/ema/ema.md) * [Calculation](indicators/averages/ema/calculation.md) * [Quality](indicators/averages/ema/quality.md) - * [EMA Charts](indicators/averages/ema/charts.md) + * [Charts](indicators/averages/ema/charts.md) * EPMA - Endpoint Moving Average * FRAMA - Fractal Adaptive Moving Average * FWMA - Fibonacci-Weighted Moving Average @@ -37,16 +79,17 @@ * [Charts](indicators/averages/sma/charts.md) * SMMA - Smoothed Moving Average * T3 - Tillson T3 Moving Average - * TEMA - Triple Exponential Moving Average + * [TEMA - Triple Exponential Moving Average](indicators/averages/tema/tema.md) + * [Calculation](indicators/averages/tema/calc.md) + * [Analysis](indicators/averages/tema/analysis.md) + * [Charts](indicators/averages/tema/charts.md) * TRIMA - Triangular Moving Average * VIDYA - Variable Index Dynamic Average * WMA - Weighted Moving Average * ZLEMA - Weighted Moving Average - * Basic Data Transforms - * [Statistics & Numerical Analysis](indicators/statistics/list.md) + * Trends + * Momentum + * Oscillators * Volatility * Volume - * Momentum & Oscillators -* Development - * [VS Code](setup/vscode.md) - * [DotPeek](setup/dotpeek.md) + diff --git a/docs/essays/realtime.md b/docs/essays/realtime.md new file mode 100644 index 00000000..13a3f46f --- /dev/null +++ b/docs/essays/realtime.md @@ -0,0 +1,43 @@ +# Historical vs. Real-time Indicators: A Tale of Two Approaches + +**Indicators for historical analysis** are like long automation trains. They zoom through a complete set of provided historical data, crunching numbers faster in the series than you can say "bullish pattern." These indicators have the luxury of seeing the big picture all at once, from the oldest to the most current data point. That is allowing them to make end-to-end calculations with a bird's-eye view of market trends. + +On the flip side, **real-time indicators** are more like surfers riding the wave of not-yet-known incoming data. They process information as it arrives, often dealing with updates and corrections to the most recent data point. + +"*Currently the Close value of the bar is at \$3.10. Actually, it is at \$3.20. No, scrape that, it is at \$3.25, which also makes a new High of the current bar.*" + +It's a bit like trying to predict the ocean's next move while you're already on the wave – exciting, but challenging! Unknown upcoming data trends alongside with the constant possiblity of corrections of the last provided value - that makes historical analysis indicators practically useless; they are fine-tuned to calculate an output on a well-known array of all known and valid historical inputs. + + +### The High-Frequency Data Dilemma + +Imagine you attach your system to an active forex or crypto ticker, and you're receiving up to 200 updates per second to form a single one-second bar. 200 updates per second is not uncommon during an active trading rally of the day, sometimes exceeding 500 updates/second. That's a lot of data to process in real-time, right? Let's break it down: + +- In one second: Up to 200 updates +- In one minute: 12,000 updates +- In one hour: 720,000 updates +- In 24 hours: 17,280,000 updates + +Now, if we're talking about gathering 24 hours of one-second bars, we're looking at `86,400` data points (60 seconds * 60 minutes * 24 hours). And every single time we receive a new update (or a signal that a new bar started so the last bar is now sealed), we need to crunch through nearly 100,000 datapoints. And do that 200 times per second. That's the calculation demand that will make even the most hard-core historical analysis indicator choke and give up. + +### The Great Calculation Showdown + +Let's compare how our historical and real-time approaches would handle this data tsunami: + +**Historical Analysis Approach:** + +- Imagine recalculating the entire history with each new or updated data point. It's like rewriting the entire encyclopedia every time you learn a new fact. With 17,280,000 updates in a day , you'd be needing: +- `17,280,000 * 86,400 = 1,492,992,000,000` calculations. +- That's nearly 1.5 trillion calculations! Your poor computer might just decide to pack its bags and go on vacation. + +**Real-time Analysis Approach:** + +- Our real-time indicator doesn't need to recalculate the entire history. It just processes each new (or updated) data point as it arrives, and spits out the result. So, we're looking at a mere 17,280,000 calculations per day, one single calculation per each update. + +### Why This Matters + +This enormous difference in calculation requirements isn't just about saving your computer from a meltdown. It's about providing traders with insights when they use tens of indicators with many parameter variations across hundreds of tracked symbols. Real-time indicators allow for quicker decision-making, more responsive trading strategies, and the ability to catch market movements as they happen. + +So, the next time someone tells you that fine-tuned historical indicators are basically faster than performance of real-time indicators, you can wow them with your newfound knowledge. Just remember, in the world of technical analysis, being real-time isn't just a feature – it's a superpower! + +**Real-time analysis is like having a super-efficient personal assistant who only tells you what's new, while historical analysis is like that friend who insists on retelling you their entire life story every time you meet up for coffee.** \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 5ff7bd52..c98425ed 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,48 +2,38 @@ - Document + QuanTAlib Documentation - + - - +
- - - - - - - + + + + + + - \ No newline at end of file + diff --git a/docs/indicators/averages/afirma/afirma.md b/docs/indicators/averages/afirma/afirma.md index ec8a714e..db119ae2 100644 --- a/docs/indicators/averages/afirma/afirma.md +++ b/docs/indicators/averages/afirma/afirma.md @@ -1,39 +1,27 @@ -## AFIRMA: Adaptive Filtering Integrated Recursive Moving Average +# AFIRMA: Autoregressive Finite Impulse Response Moving Average -### Concept +## Concept -AFIRMA combines elements of simple moving averages (`SMA`) with adaptive filtering techniques from signal processing. It aims to provide a more responsive indicator that can quickly adjust to market changes while maintaining stability. +AFIRMA indicator is a hybrid moving average that combines the benefits of digital filtering and cubic spline fitting to provide a smooth and accurate representation of price movement without significant time lag. -### Origin +## Origin -AFIRMA (Adaptive Filtering Integrated Recursive Moving Average) was developed by Clive Bowsher and Roland Meeks in their 2008 paper titled "*The Dynamics of Economic Functions: Modeling and Forecasting the Yield Curve.*" It was created as an improvement over traditional moving averages, designed to adapt more quickly to changes in financial time series data. +The AFIRMA indicator is based on the principles of digital signal processing and curve fitting. It was developed to address the limitations of traditional moving averages, which often suffer from time lag or fail to accurately track price movements. -### Key Features +## Key Features -1. **Adaptive Nature**: AFIRMA adjusts its behavior based on recent price movements, allowing it to respond more quickly to significant changes. -2. **Error-based Adaptation**: It uses the error between its last output and the current input to determine how much to adapt. -3. **Customizable Sensitivity**: The alpha parameter allows fine-tuning the indicator's responsiveness. +- **Digital Filter**: The AFIRMA indicator uses a digital filter to smooth out price movements. +- **Cubic Spline Fitting**: The latest candlesticks are smoothed using cubic spline fitting with the least square method to ensure a seamless transition. +- **Combined Moving Average**: The indicator combines the digital filter and cubic spline fitting to create a smooth moving average that accurately tracks prices without time lag. +- **Customizable Parameters**: The AFIRMA indicator allows users to adjust the Periods, Taps, and Window parameters to fine-tune the indicator's performance. -### Usage +## Advantages -1. **Trend Identification**: AFIRMA can help identify the start or end of trends more quickly than traditional moving averages. -2. **Signal Generation**: Traders may use crossovers of AFIRMA with price or other indicators to generate buy/sell signals. -3. **Dynamic Support/Resistance**: The AFIRMA line can act as a dynamic support or resistance level. -4. **Volatility Analysis**: The adaptive nature of AFIRMA can provide insights into market volatility. +- **Accurate Price Tracking**: The AFIRMA indicator provides a smooth and accurate representation of price movement without time lag. +- **Hybrid Approach**: The combination of digital filtering and cubic spline fitting provides a unique and effective approach to moving average calculation. -### Advantages +## Considerations -- More responsive to market changes compared to traditional moving averages. -- Reduces lag typically associated with moving averages. -- Customizable through its `alpha` parameter to suit different market conditions or trading styles. - -### Considerations - -- **Adaptive Factor**: Alpha serves as the base adaptive factor. It has a range between 0.0 and 1.0 that determines how quickly the AFIRMA responds to changes in the input data. -- **Error Sensitivity**: Alpha is used in calculating the adaptive factor, which is based on the error between the current input and the last AFIRMA value. -- **Balancing Stability and Responsiveness**: A smaller alpha (closer to 0.0) makes the AFIRMA more stable but less responsive to recent changes. -A larger alpha (closer to 1) makes the AFIRMA more responsive to recent changes but potentially more volatile. -- **Fine-tuning the Indicator**: - - In trending markets, a higher alpha might be preferred to capture price movements more quickly. - - In ranging markets, a lower alpha might be better to reduce false signals from price noise. -- **Adaptive Nature**: The use of alpha allows AFIRMA to adapt its behavior based on recent price movements. When there are significant changes (large errors), the adaptive factor increases, allowing AFIRMA to adjust more quickly. \ No newline at end of file +**Complexity**: The AFIRMA indicator is a complex filter that requires some understanding of digital signal processing and curve fitting to use it right. +- **Parameter Optimization**: Finding the optimal parameters for the AFIRMA indicator may require some experimentation and testing. +- **Computational Resources**: The AFIRMA indicator is computationally more intensive than traditional moving averages due to the use of cubic spline fitting. \ No newline at end of file diff --git a/docs/indicators/averages/afirma/analysis.md b/docs/indicators/averages/afirma/analysis.md new file mode 100644 index 00000000..8392e240 --- /dev/null +++ b/docs/indicators/averages/afirma/analysis.md @@ -0,0 +1,60 @@ +# AFIRMA: Benchmark Analysis + +This analysis evaluates the Autoregressive Finite Impulse Response Moving Average (AFIRMA) across four core benchmarks: accuracy, timeliness, overshooting, and smoothness. These benchmarks provide a comprehensive view of AFIRMA's performance characteristics and serve as a basis for comparison with other moving averages. + +## Accuracy (closeness to the original data) + +AFIRMA generally exhibits high accuracy in representing the original price data due to its sophisticated approach combining digital filtering and cubic spline fitting. + +- **Strengths**: + - The digital filter component helps to reduce noise while preserving important price trends. + - Cubic spline fitting for recent candlesticks ensures that the most current price movements are accurately represented. + +- **Considerations**: + - Accuracy can vary based on parameter settings. Incorrect parameter selection might lead to over-smoothing or under-smoothing, potentially reducing accuracy. + - In highly volatile markets, AFIRMA may sacrifice some accuracy for smoothness, especially if the parameters are set to prioritize noise reduction. + +## Timeliness (amount of lag) + +AFIRMA is designed to minimize lag, which is one of its key advantages over traditional moving averages. + +- **Strengths**: + - The combination of ARMA modeling and FIR filtering allows AFIRMA to respond quickly to price changes. + - Cubic spline fitting of recent data points further reduces lag for the most current price movements. + +- **Considerations**: + - While AFIRMA generally has less lag than traditional MAs, it's not entirely lag-free. Some minimal lag may still be present, especially with longer period settings. + - The amount of lag can be influenced by parameter settings. Optimizing for minimal lag might come at the cost of increased noise sensitivity. + +## Overshooting (overcompensation during reversals) + +AFIRMA's design helps to mitigate overshooting during price reversals, but the extent can vary based on settings and market conditions. + +- **Strengths**: + - The digital filtering component helps to dampen extreme price movements, reducing the likelihood of significant overshooting. + - Cubic spline fitting allows for smoother transitions during reversals, potentially minimizing overshoot. + +- **Considerations**: + - Overshooting can still occur, especially in markets with sudden, sharp reversals. + - The degree of overshooting can be influenced by parameter settings. More aggressive settings might increase responsiveness but also the risk of overshooting. + +## Smoothness (continuous 2nd derivative, less jagged flow) + +AFIRMA generally produces a smoother line than many traditional moving averages, which is one of its defining characteristics. + +- **Strengths**: + - The digital filtering component effectively smooths out minor price fluctuations and noise. + - Cubic spline fitting ensures a smooth transition between historical and current data points. + - The combination of these techniques results in a visually smooth line that can make trend identification easier. + +- **Considerations**: + - The degree of smoothness can be adjusted through parameter settings. Excessive smoothing might lead to a loss of responsiveness to genuine price changes. + - In some cases, the smooth line might mask short-term volatility that could be relevant for certain trading strategies. + +## Conclusion + +AFIRMA demonstrates strong performance across all four benchmarks, particularly excelling in accuracy, timeliness, and smoothness. Its complex approach allows it to balance these often competing characteristics more effectively than many traditional moving averages. + +However, it's important to note that AFIRMA's performance can be significantly influenced by its parameter settings. Optimal use of AFIRMA requires careful tuning of these parameters to balance accuracy, timeliness, overshooting resistance, and smoothness for the specific asset and timeframe being analyzed. + +When compared to other moving averages, AFIRMA generally offers superior or comparable performance across these benchmarks. However, this comes at the cost of increased complexity and computational requirements. Traders and analysts should weigh these factors when deciding whether to incorporate AFIRMA into their technical analysis toolkit. \ No newline at end of file diff --git a/docs/indicators/averages/afirma/calc.md b/docs/indicators/averages/afirma/calc.md new file mode 100644 index 00000000..fcc2255d --- /dev/null +++ b/docs/indicators/averages/afirma/calc.md @@ -0,0 +1,67 @@ +# The Math Behind AFIRMA + +## Components of AFIRMA + +AFIRMA is a hybrid beast, combining two main components: + +- Autoregressive Moving Average (ARMA) +- Finite Impulse Response (FIR) filter + +### ARMA Component + +$ X_t = c + \epsilon_t + \sum_{i=1}^p \phi_i X_{t-i} + \sum_{j=1}^q \theta_j \epsilon_{t-j} $ + +Where: +- $X_t$ is the time series value at time $t$
+- $c$ is a constant
+- $\phi_i$ are the parameters of the autoregressive term
+- $\theta_j$ are the parameters of the moving average term
+- $\epsilon_t$ is white noise
+ +### FIR Component + +$ y[n] = \sum_{i=0}^{N-1} b_i \cdot x[n-i] $ + +Where: +- $y[n]$ is the output signal +- $x[n]$ is the input signal +- $b_i$ are the filter coefficients +- $N$ is the filter order + +### AFIRMA: Putting It All Together + +AFIRMA combines these components and adds cubic spline fitting to the mix. The general form can be expressed as: + +$ AFIRMA_t = ARMA_t + FIR_t + CS_t $ + +Where: +- $ARMA_t$ is the ARMA component at time $t$ +- $FIR_t$ is the FIR component at time $t$ +- $CS_t$ is the cubic spline fitting component at time $t$ + +### Digital Filtering Process + +- The price data is passed through the digital filter to smooth out fluctuations. +- The filter coefficients are optimized based on the specified parameters (Periods, Taps, Window). + +### Cubic Spline Fitting + +For the most recent bars: + +- A cubic spline is fitted to the data points using the least squares method. +- This ensures a smooth transition between the filtered data and the most recent price movements. + +### Parameter Definitions + +The AFIRMA indicator allows for the adjustment of three main parameters: + +- **Periods**: Affects the overall smoothness of the indicator. +- *Taps*: Influences the complexity of the digital filter. +- *Window*: Determines the number of recent bars to which the cubic spline fitting is applied. + +### Computational Process + +- Apply the ARMA model to the price data. +- Pass the result through the FIR filter. +- Apply cubic spline fitting to the most recent data points. +- Combine the results to produce the final AFIRMA value. diff --git a/docs/indicators/averages/afirma/charts.dib b/docs/indicators/averages/afirma/charts.dib index d1c8c640..7d93d388 100644 --- a/docs/indicators/averages/afirma/charts.dib +++ b/docs/indicators/averages/afirma/charts.dib @@ -42,20 +42,22 @@ Dictionary Data = new Dictionary #!csharp String Name = "AFIRMA"; -int p = 10; -double alpha = 0.5; -Func Indicator = period => new Afirma(period,alpha); +int taps = 6; +int periods = 6; +Afirma.WindowType window = Afirma.WindowType.BlackmanHarris; + +Func Indicator = (taps, periods, windows) => new Afirma(taps: taps, periods: periods, window: window); foreach (var item in Data) { string Signal = item.Key; double[] Input = item.Value; TSeries Output = new(); - var ma = Indicator(p); + var ma = Indicator(taps, periods, window); foreach (var value in Input) { Output.Add(ma.Calc(value)); } Plot plt = new(); var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; - plt.Title($"{Signal} - {Name}({p}, {alpha})"); + plt.Title($"{Signal} - {Name}({taps}, {periods}, {window.ToString()})"); plt.Display(); - plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); + plt.SaveSvg($"img/{Name}{taps}_{Signal}.svg", 450, 300); } diff --git a/docs/indicators/averages/afirma/charts.md b/docs/indicators/averages/afirma/charts.md index d3b95b46..d4b19d30 100644 --- a/docs/indicators/averages/afirma/charts.md +++ b/docs/indicators/averages/afirma/charts.md @@ -1,3 +1,3 @@ -# AFIRMA Charts +# AFIRMA: Charts -![](img/AFIRMA10_Spike.svg) ![](img/AFIRMA10_Impulse.svg) ![](img/AFIRMA10_Triangle.svg) ![](img/AFIRMA10_Sawtooth.svg) ![](img/AFIRMA10_Sine.svg) ![](img/AFIRMA10_Chirp.svg) ![](img/AFIRMA10_White.svg) ![](img/AFIRMA10_Gauss.svg) ![](img/AFIRMA10_B.svg) ![](img/AFIRMA10_HF.svg) ![](img/AFIRMA10_ImpulseHF.svg) ![](img/AFIRMA10_SawtoothHF.svg) ![](img/AFIRMA10_SineG.svg) ![](img/AFIRMA10_ChirpG.svg) ![](img/AFIRMA10_Complex.svg) ![](img/AFIRMA10_Market.svg) +![](img/AFIRMA6_Spike.svg) ![](img/AFIRMA6_Impulse.svg) ![](img/AFIRMA6_Triangle.svg) ![](img/AFIRMA6_Sawtooth.svg) ![](img/AFIRMA6_Sine.svg) ![](img/AFIRMA6_Chirp.svg) ![](img/AFIRMA6_White.svg) ![](img/AFIRMA6_Gauss.svg) ![](img/AFIRMA6_B.svg) ![](img/AFIRMA6_HF.svg) ![](img/AFIRMA6_ImpulseHF.svg) ![](img/AFIRMA6_SawtoothHF.svg) ![](img/AFIRMA6_SineG.svg) ![](img/AFIRMA6_ChirpG.svg) ![](img/AFIRMA6_Complex.svg) ![](img/AFIRMA6_Market.svg) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_B.svg b/docs/indicators/averages/afirma/img/AFIRMA6_B.svg new file mode 100644 index 00000000..43dcf35a --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_B.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + B - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Chirp.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Chirp.svg similarity index 78% rename from docs/indicators/averages/afirma/img/AFIRMA10_Chirp.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Chirp.svg index 1c8e44c7..b1826d0d 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Chirp.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Chirp.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - Chirp - AFIRMA(10, 0.5) + + Chirp - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_ChirpG.svg b/docs/indicators/averages/afirma/img/AFIRMA6_ChirpG.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_ChirpG.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_ChirpG.svg index 62dd210d..2c61faa3 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_ChirpG.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_ChirpG.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -342,7 +342,7 @@ - - ChirpG - AFIRMA(10, 0.5) + + ChirpG - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Complex.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Complex.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_Complex.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Complex.svg index ed8b1c42..4a1b92bc 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Complex.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Complex.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -327,7 +327,7 @@ - - Complex - AFIRMA(10, 0.5) + + Complex - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_Gauss.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Gauss.svg new file mode 100644 index 00000000..6e6747fb --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_HF.svg b/docs/indicators/averages/afirma/img/AFIRMA6_HF.svg new file mode 100644 index 00000000..ec93b5b8 --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_HF.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + HF - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Impulse.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Impulse.svg similarity index 89% rename from docs/indicators/averages/afirma/img/AFIRMA10_Impulse.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Impulse.svg index 45ddbb94..878244c4 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Impulse.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Impulse.svg @@ -3,10 +3,10 @@ - + - + @@ -81,26 +81,26 @@ - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -125,15 +125,15 @@ - - - - - - - - - + + + + + + + + + @@ -332,7 +332,7 @@ - - Impulse - AFIRMA(10, 0.5) + + Impulse - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_ImpulseHF.svg b/docs/indicators/averages/afirma/img/AFIRMA6_ImpulseHF.svg new file mode 100644 index 00000000..bff64afa --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_Market.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Market.svg new file mode 100644 index 00000000..6f006235 --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Sawtooth.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Sawtooth.svg similarity index 84% rename from docs/indicators/averages/afirma/img/AFIRMA10_Sawtooth.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Sawtooth.svg index ec5cadb8..a4d68182 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Sawtooth.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Sawtooth.svg @@ -3,10 +3,10 @@ - + - + @@ -81,61 +81,61 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -349,7 +349,7 @@ - - Sawtooth - AFIRMA(10, 0.5) + + Sawtooth - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_SawtoothHF.svg b/docs/indicators/averages/afirma/img/AFIRMA6_SawtoothHF.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_SawtoothHF.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_SawtoothHF.svg index 392508d4..7cdb3f2b 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_SawtoothHF.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_SawtoothHF.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -326,7 +326,7 @@ - - SawtoothHF - AFIRMA(10, 0.5) + + SawtoothHF - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Sine.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Sine.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_Sine.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Sine.svg index 5d391a00..c08c7024 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Sine.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Sine.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - Sine - AFIRMA(10, 0.5) + + Sine - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_SineG.svg b/docs/indicators/averages/afirma/img/AFIRMA6_SineG.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_SineG.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_SineG.svg index 4f8b6d2b..d731a839 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_SineG.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_SineG.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -340,7 +340,7 @@ - - SineG - AFIRMA(10, 0.5) + + SineG - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Spike.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Spike.svg similarity index 89% rename from docs/indicators/averages/afirma/img/AFIRMA10_Spike.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Spike.svg index ef3d9095..004c96d0 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Spike.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Spike.svg @@ -3,10 +3,10 @@ - + - + @@ -81,26 +81,26 @@ - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -125,16 +125,16 @@ - - - - - - - - - - + + + + + + + + + + @@ -332,7 +332,7 @@ - - Spike - AFIRMA(10, 0.5) + + Spike - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Triangle.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Triangle.svg similarity index 80% rename from docs/indicators/averages/afirma/img/AFIRMA10_Triangle.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Triangle.svg index f4fc099c..e675197d 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Triangle.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Triangle.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -349,7 +349,7 @@ - - Triangle - AFIRMA(10, 0.5) + + Triangle - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_White.svg b/docs/indicators/averages/afirma/img/AFIRMA6_White.svg new file mode 100644 index 00000000..b649a062 --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_White.svg @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/alma/analysis.md b/docs/indicators/averages/alma/analysis.md new file mode 100644 index 00000000..87bf847d --- /dev/null +++ b/docs/indicators/averages/alma/analysis.md @@ -0,0 +1,51 @@ +# ALMA: Benchmark Analysis + +This analysis evaluates the Arnaud Legoux Moving Average (ALMA) across four core benchmarks: accuracy, timeliness, overshooting, and smoothness. These benchmarks provide a comprehensive view of ALMA's performance characteristics and serve as a basis for comparison with other moving averages. + +## Accuracy (closeness to the original data) + +ALMA generally exhibits good accuracy in representing the original price data due to its Gaussian distribution-based weighting system. + +- **Strengths**: + - The Gaussian distribution weighting helps to reduce noise while preserving important price trends. + - The offset parameter allows for fine-tuning of the balance between recent and historical data representation. + +- **Considerations**: + - Accuracy can vary based on parameter settings. Incorrect parameter selection might lead to over-smoothing or under-smoothing, potentially reducing accuracy. + - In highly volatile markets, ALMA may sacrifice some accuracy for smoothness, especially if the sigma parameter is set to prioritize noise reduction. + +## Timeliness (amount of lag) + +ALMA is designed to minimize lag, which is one of its key advantages over traditional moving averages. + +- **Strengths**: + - The offset parameter allows ALMA to be more responsive to recent price changes, potentially reducing lag. + - The ability to adjust the window size provides flexibility in balancing timeliness and stability. + +- **Considerations**: + - While ALMA generally has less lag than traditional MAs, it's not entirely lag-free. Some minimal lag may still be present, especially with larger window sizes. + - The amount of lag can be influenced by parameter settings. Optimizing for minimal lag might come at the cost of increased noise sensitivity. + +## Overshooting (overcompensation during reversals) + +ALMA's design helps to mitigate overshooting during price reversals, but the extent can vary based on settings and market conditions. + +- **Strengths**: + - The Gaussian distribution weighting helps to dampen extreme price movements, reducing the likelihood of significant overshooting. + - The sigma parameter allows for control over the smoothness of transitions, potentially minimizing overshoot. + +- **Considerations**: + - Overshooting can still occur, especially in markets with sudden, sharp reversals. + - The degree of overshooting can be influenced by parameter settings. More aggressive settings (lower sigma, higher offset) might increase responsiveness but also the risk of overshooting. + +## Smoothness (continuous 2nd derivative, less jagged flow) + +ALMA generally produces a smoother line than many traditional moving averages, which is one of its defining characteristics. + +- **Strengths**: + - The Gaussian distribution weighting effectively smooths out minor price fluctuations and noise. + - The sigma parameter provides direct control over the smoothness of the line. + - The resulting smooth line can make trend identification easier. + +- **Considerations**: + - The degree of smoothness can be adjusted through parameter settings. \ No newline at end of file diff --git a/docs/indicators/averages/alma/calc.md b/docs/indicators/averages/alma/calc.md new file mode 100644 index 00000000..7c9bc48b --- /dev/null +++ b/docs/indicators/averages/alma/calc.md @@ -0,0 +1,45 @@ +# The Math Behind ALMA + +## Components of ALMA + +ALMA is a single-formula moving average that incorporates elements of several advanced techniques: + +- Gaussian distribution +- Weighted moving average +- Offset parameter + +### ALMA Formula + +$ ALMA_t = \sum_{i=0}^{n-1} w_i \cdot P_{t-i} $ + +Where: +- $ALMA_t$ is the ALMA value at time $t$ +- $n$ is the window size (number of periods) +- $P_{t-i}$ is the price at time $t-i$ +- $w_i$ are the weights + +### Weight Calculation + +The weights $w_i$ are calculated using a Gaussian distribution function with an offset: + +$ w_i = \exp\left(-\frac{(i - m)^2}{2s^2}\right) $ + +Where: +- $i$ is the position of the price in the window (0 to $n-1$) +- $m$ is the offset of the Gaussian distribution, calculated as $m = \text{floor}(offset \cdot (n - 1))$ +- $s$ is the standard deviation of the Gaussian distribution, calculated as $s = \frac{n}{sigma}$ + +### Parameter Definitions + +ALMA uses three main parameters: + +- **Window size** ($n$): Affects the overall reactivity of the indicator. +- **Offset**: Influences the lag of the moving average. Lower values reduce lag but may increase noise. +- **Sigma**: Controls the smoothness of the indicator. Higher values increase smoothness but may increase lag. + +### Computational Process + +For each new data point: +- Calculate the weights for the entire window. +- Apply these weights to the most recent $n$ prices. +- Sum the weighted prices to produce the final ALMA value. diff --git a/docs/indicators/averages/dema/analysis.md b/docs/indicators/averages/dema/analysis.md new file mode 100644 index 00000000..ee4ada70 --- /dev/null +++ b/docs/indicators/averages/dema/analysis.md @@ -0,0 +1,65 @@ +# DEMA: Benchmark Analysis + +This analysis evaluates the Double Exponential Moving Average (DEMA) across four core benchmarks: accuracy, timeliness, overshooting, and smoothness. + +## Accuracy (closeness to the original data) + +DEMA generally provides a good balance between accuracy and smoothing. + +- **Strengths**: + - The double smoothing process helps to reduce noise while preserving important price trends. + - More accurate than a simple EMA, especially during trend changes. + +- **Considerations**: + - In highly volatile markets, DEMA may sacrifice some accuracy for smoothness. + - Accuracy can vary based on the period setting. Shorter periods increase accuracy but may introduce more noise. + +## Timeliness (amount of lag) + +DEMA is designed to reduce lag compared to traditional moving averages, which is one of its key advantages. + +- **Strengths**: + - The double smoothing formula effectively reduces lag compared to standard EMAs. + - Responds more quickly to price changes than simple or exponential moving averages. + +- **Considerations**: + - While DEMA has less lag than traditional MAs, it's not entirely lag-free. + - Shorter periods reduce lag but may increase sensitivity to noise. + +## Overshooting (overcompensation during reversals) + +DEMA is known for significant overshooting during price reversals, which is one of its main drawbacks. + +- **Weaknesses**: + - Prone to substantial overshooting, especially during sharp price reversals. + - The double exponential smoothing, while reducing lag, can exaggerate price movements during trend changes. + +- **Considerations**: + - Overshooting is particularly pronounced in volatile markets or during sudden trend reversals. + - Shorter periods may further increase the risk and magnitude of overshooting. + - This characteristic can lead to false signals or exaggerated price projections, potentially misleading traders. + +## Smoothness (continuous 2nd derivative, less jagged flow) + +DEMA produces a relatively smooth line, balancing smoothness with responsiveness. + +- **Strengths**: + - Smoother than a standard EMA, making trend identification easier. + - The double smoothing process effectively reduces minor fluctuations. + +- **Considerations**: + - Less smooth than higher-order moving averages or those with explicit smoothing parameters. + - The degree of smoothness is primarily controlled by the period setting, offering less flexibility than some advanced moving averages. + +## Conclusion + +DEMA demonstrates mixed performance across the four benchmarks. It excels in reducing lag and maintains a good degree of smoothness, but its tendency to overshoot significantly during price reversals is a major drawback. + +DEMA's performance is influenced by its single parameter (the period). While this simplicity is an advantage for ease of use, it also means there's less flexibility to mitigate its overshooting tendency. + +Compared to more complex moving averages like AFIRMA or ALMA, DEMA offers simplicity and excellent lag reduction. However, its proneness to overshooting can make it less reliable during volatile market conditions or during trend reversals. + +Traders and analysts should carefully consider DEMA's strengths and weaknesses. While it offers improved lag reduction over simple moving averages, its overshooting characteristic can lead to false signals. This makes it potentially risky to use on its own, especially in volatile markets. +DEMA might be most effectively used in conjunction with other indicators that can help confirm signals and mitigate the risk of false readings due to overshooting. It may be particularly useful in strongly trending markets where its lag reduction is beneficial and the risk of reversal (and thus overshooting) is lower. + +In summary, DEMA's simplicity and lag reduction make it an interesting tool, but its tendency to overshoot means it should be used with caution and preferably as part of a broader analytical approach rather than as a standalone indicator. \ No newline at end of file diff --git a/docs/indicators/averages/dema/calc.md b/docs/indicators/averages/dema/calc.md new file mode 100644 index 00000000..bf9035dc --- /dev/null +++ b/docs/indicators/averages/dema/calc.md @@ -0,0 +1,48 @@ +# The Math Behind DEMA + +## Components of DEMA + +DEMA is composed of two main components: + +1. Exponential Moving Average (EMA) +2. A "double smoothing" factor + +Let's break these down: + +### EMA Calculation + +The Exponential Moving Average (EMA) is calculated as: + +$ EMA_t = \alpha \cdot P_t + (1 - \alpha) \cdot EMA_{t-1} $ + +Where: +- $EMA_t$ is the EMA value at time $t$ +- $P_t$ is the price at time $t$ +- $\alpha$ is the smoothing factor, calculated as $\frac{2}{n+1}$ +- $n$ is the number of periods + +### DEMA Formula + +The DEMA is then calculated using the following formula: + +$ DEMA_t = 2 \cdot EMA_t - EMA(EMA_t) $ + +Where: +- $DEMA_t$ is the DEMA value at time $t$ +- $EMA_t$ is the EMA of the price +- $EMA(EMA_t)$ is the EMA of the EMA + +## Calculation Process + +1. Calculate the EMA of the price series. +2. Calculate another EMA on the result of step 1. +3. Multiply the first EMA by 2. +4. Subtract the second EMA from the result of step 3. + +This process effectively reduces lag while maintaining smoothness. + +## Parameter + +DEMA uses a single parameter: + +- **Period** ($n$): Determines the number of periods used in the EMA calculations. This affects the overall reactivity and smoothness of the indicator. diff --git a/docs/indicators/averages/dsma/analysis.md b/docs/indicators/averages/dsma/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/dsma/calc.md b/docs/indicators/averages/dsma/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/dwma/analysis.md b/docs/indicators/averages/dwma/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/dwma/calc.md b/docs/indicators/averages/dwma/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/ema/analysis.md b/docs/indicators/averages/ema/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/ema/calc.md b/docs/indicators/averages/ema/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/sma/calc.md b/docs/indicators/averages/sma/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/tema/analysis.md b/docs/indicators/averages/tema/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/tema/calc.md b/docs/indicators/averages/tema/calc.md new file mode 100644 index 00000000..631b5647 --- /dev/null +++ b/docs/indicators/averages/tema/calc.md @@ -0,0 +1,52 @@ +# The Math Behind TEMA + +## Components of TEMA + +TEMA is composed of three main components: + +1. Exponential Moving Average (EMA) +2. A "triple smoothing" factor + +Let's break these down: + +### EMA Calculation + +The Exponential Moving Average (EMA) is calculated as: + +$ EMA_t = \alpha \cdot P_t + (1 - \alpha) \cdot EMA_{t-1} $ + +Where: +- $EMA_t$ is the EMA value at time $t$ +- $P_t$ is the price at time $t$ +- $\alpha$ is the smoothing factor, calculated as $\frac{2}{n+1}$ +- $n$ is the number of periods + +### TEMA Formula + +The TEMA is then calculated using the following formula: + +$ TEMA_t = 3 \cdot EMA_t - 3 \cdot EMA(EMA_t) + EMA(EMA(EMA_t)) $ + +Where: +- $TEMA_t$ is the TEMA value at time $t$ +- $EMA_t$ is the EMA of the price +- $EMA(EMA_t)$ is the EMA of the EMA +- $EMA(EMA(EMA_t))$ is the EMA of the EMA of the EMA + +## Calculation Process + +1. Calculate the EMA of the price series (EMA1). +2. Calculate another EMA on the result of step 1 (EMA2). +3. Calculate a third EMA on the result of step 2 (EMA3). +4. Multiply EMA1 by 3. +5. Multiply EMA2 by 3. +6. Subtract EMA2 * 3 from EMA1 * 3. +7. Add EMA3 to the result. + +This process effectively reduces lag while maintaining smoothness and attempting to minimize overshooting. + +## Parameter + +TEMA uses a single parameter: + +- **Period** ($n$): Determines the number of periods used in the EMA calculations. This affects the overall reactivity and smoothness of the indicator. \ No newline at end of file diff --git a/docs/indicators/averages/tema/charts.dib b/docs/indicators/averages/tema/charts.dib new file mode 100644 index 00000000..956337cb --- /dev/null +++ b/docs/indicators/averages/tema/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "TEMA"; +int p = 10; +Func Indicator = period => new Tema(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/tema/charts.md b/docs/indicators/averages/tema/charts.md new file mode 100644 index 00000000..a7309bfd --- /dev/null +++ b/docs/indicators/averages/tema/charts.md @@ -0,0 +1,3 @@ +# TEMA Charts + +![](img/TEMA10_Spike.svg) ![](img/TEMA10_Impulse.svg) ![](img/TEMA10_Triangle.svg) ![](img/TEMA10_Sawtooth.svg) ![](img/TEMA10_Sine.svg) ![](img/TEMA10_Chirp.svg) ![](img/TEMA10_White.svg) ![](img/TEMA10_Gauss.svg) ![](img/TEMA10_B.svg) ![](img/TEMA10_HF.svg) ![](img/TEMA10_ImpulseHF.svg) ![](img/TEMA10_SawtoothHF.svg) ![](img/TEMA10_SineG.svg) ![](img/TEMA10_ChirpG.svg) ![](img/TEMA10_Complex.svg) ![](img/TEMA10_Market.svg) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_B.svg b/docs/indicators/averages/tema/img/TEMA10_B.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_B.svg rename to docs/indicators/averages/tema/img/TEMA10_B.svg index 08fbe8bf..e0b2fac7 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_B.svg +++ b/docs/indicators/averages/tema/img/TEMA10_B.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - B - AFIRMA(10, 0.5) + + B - TEMA(10) diff --git a/docs/indicators/averages/tema/img/TEMA10_Chirp.svg b/docs/indicators/averages/tema/img/TEMA10_Chirp.svg new file mode 100644 index 00000000..88cd9d6e --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Chirp.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Chirp - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_ChirpG.svg b/docs/indicators/averages/tema/img/TEMA10_ChirpG.svg new file mode 100644 index 00000000..0cb96318 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_ChirpG.svg @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Complex.svg b/docs/indicators/averages/tema/img/TEMA10_Complex.svg new file mode 100644 index 00000000..fa50d5dd --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Complex.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - TEMA(10) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Gauss.svg b/docs/indicators/averages/tema/img/TEMA10_Gauss.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_Gauss.svg rename to docs/indicators/averages/tema/img/TEMA10_Gauss.svg index 98190bd9..f56510ae 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Gauss.svg +++ b/docs/indicators/averages/tema/img/TEMA10_Gauss.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -321,7 +321,7 @@ - - Gauss - AFIRMA(10, 0.5) + + Gauss - TEMA(10) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_HF.svg b/docs/indicators/averages/tema/img/TEMA10_HF.svg similarity index 78% rename from docs/indicators/averages/afirma/img/AFIRMA10_HF.svg rename to docs/indicators/averages/tema/img/TEMA10_HF.svg index c2259c57..6227f0fc 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_HF.svg +++ b/docs/indicators/averages/tema/img/TEMA10_HF.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - HF - AFIRMA(10, 0.5) + + HF - TEMA(10) diff --git a/docs/indicators/averages/tema/img/TEMA10_Impulse.svg b/docs/indicators/averages/tema/img/TEMA10_Impulse.svg new file mode 100644 index 00000000..e4d9cf92 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Impulse.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + 1.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - TEMA(10) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_ImpulseHF.svg b/docs/indicators/averages/tema/img/TEMA10_ImpulseHF.svg similarity index 78% rename from docs/indicators/averages/afirma/img/AFIRMA10_ImpulseHF.svg rename to docs/indicators/averages/tema/img/TEMA10_ImpulseHF.svg index 4a5c8479..90c19c1d 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_ImpulseHF.svg +++ b/docs/indicators/averages/tema/img/TEMA10_ImpulseHF.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -314,7 +314,7 @@ - - ImpulseHF - AFIRMA(10, 0.5) + + ImpulseHF - TEMA(10) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Market.svg b/docs/indicators/averages/tema/img/TEMA10_Market.svg similarity index 80% rename from docs/indicators/averages/afirma/img/AFIRMA10_Market.svg rename to docs/indicators/averages/tema/img/TEMA10_Market.svg index cf860443..f3b8bef4 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Market.svg +++ b/docs/indicators/averages/tema/img/TEMA10_Market.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -351,7 +351,7 @@ - - Market - AFIRMA(10, 0.5) + + Market - TEMA(10) diff --git a/docs/indicators/averages/tema/img/TEMA10_Sawtooth.svg b/docs/indicators/averages/tema/img/TEMA10_Sawtooth.svg new file mode 100644 index 00000000..4481c708 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Sawtooth.svg @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -5 + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_SawtoothHF.svg b/docs/indicators/averages/tema/img/TEMA10_SawtoothHF.svg new file mode 100644 index 00000000..9a6da825 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_SawtoothHF.svg @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -10 + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Sine.svg b/docs/indicators/averages/tema/img/TEMA10_Sine.svg new file mode 100644 index 00000000..9c3950fa --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Sine.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Sine - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_SineG.svg b/docs/indicators/averages/tema/img/TEMA10_SineG.svg new file mode 100644 index 00000000..ac43a37a --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_SineG.svg @@ -0,0 +1,347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Spike.svg b/docs/indicators/averages/tema/img/TEMA10_Spike.svg new file mode 100644 index 00000000..de7a15bf --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Spike.svg @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Triangle.svg b/docs/indicators/averages/tema/img/TEMA10_Triangle.svg new file mode 100644 index 00000000..1a6e063e --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - TEMA(10) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_White.svg b/docs/indicators/averages/tema/img/TEMA10_White.svg similarity index 80% rename from docs/indicators/averages/afirma/img/AFIRMA10_White.svg rename to docs/indicators/averages/tema/img/TEMA10_White.svg index d23c6dd3..69601237 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_White.svg +++ b/docs/indicators/averages/tema/img/TEMA10_White.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -329,7 +329,7 @@ - - White - AFIRMA(10, 0.5) + + White - TEMA(10) diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md index 8de0cd95..e68c12a9 100644 --- a/docs/indicators/indicators.md +++ b/docs/indicators/indicators.md @@ -4,22 +4,144 @@ ✔️= Validation tests passed
❌= Issue -|**BASIC TRANSFORMS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| -|--|:--:|:--:|:--:|:--:|:--:| -|OC2 - Midpoint price|️`.OC2`|`CandlePart.OC2`|`MidPoint`|| -|HL2 - Median Price|️`.HL2`|`CandlePart.HL2`|`MedPrice`|| -|HLC3 - Typical Price|️`.HLC3`|`CandlePart.HLC3`|`TypPrice`|| -|OHL3 - Mean Price|`️.OHL3`|`CandlePart.OHL3`||| -|OHLC4 - Average Price|`️.OHLC4`|`CandlePart.OHLC4`|`AvgPrice`|| -|HLCC4 - Weighted Price|`️.HLCC4`||`WclPrice`|| -|
|||| -|**AVERAGES & TRENDS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| -|AFIRMA - Autoregressive Finite Impulse Response Moving Average|`✔️`|||| -|ALMA - Arnaud Legoux Moving Average|`Alma`|`✔️`||| -|⭐DEMA - Double EMA Average|`Dema`|`⭐`|`⭐`|`⭐`|| + +|**MOMENTUM INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|--|:--:|:--:|:--:| +|*DMI - Directional Movement Index|`?`|GetDmi|| +|*DMX - Jurik Directional Movement Index|`?`||| +|*MOM - Momentum|`?`||| +|*VEL - Jurik Signal Velocity|`?`||| +|ADX - Average Directional Movement Index|`?`|GetAdx|Adx| +|ADXR - Average Directional Movement Index|`?`|Rating|Adxr| +|APO - Absolute Price Oscillator|`?`|Apo|| +|DPO - Detrended Price Oscillator|`?`|GetDpo|| +|MACD - Movign Average Convergence/Divergence|`?`||| +|PO - Price Oscillator|`?`||| +|PPO - Percentage Price Oscillator|`?`||| +|PMO - Price Momentum Oscillator|`?`|GetPmo|| +|PRS - Price Relative Strength|`?`|GetPrs|| +|ROC - Rate of Change|`?`|GetRoc|| +|TRIX - 1-day ROC of TEMA|`?`|GetTrix|| +|VORTEX - Vortex Indicator|`?`||| +
+|**VOLATILITY INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|ADR - Average Daily Range|||| +|ANDREW - Andrew's Pitchfork|||| +|ATR - Average True Range|`Atr`|GetAtr|Atr| +|ATRP - Average True Range Percent|||| +|ATRSTOP - ATR Trailing Stop ||GetAtrStop|| +|BBANDS - Bollinger Bands®||BollingerBands|| +|CHAND - Chandelier Exit||GetChandelier|| +|CVI - Chaikins Volatility|||| +|DON - Donchian Channels||GetDonchian|| +|FCB - Fractal Chaos Bands||GetFcb|| +|HV - Historical Volatility|||| +|ICH - Ichimoku Cloud||GetIchimoku|| +|KEL - Keltner Channels||GetKeltner|| +|NATR - Normalized Average True Range||GetAtr|| +|CHN - Price Channel Indicator|||| +|SAR - Parabolic Stop and Reverse||GetParabolicSar|| +|STARC - Starc Bands||GetStarcBands|| +|TR - True Range|||| +|UI - Ulcer Index||GetUlcerIndex|| +|VSTOP - Volatility Stop||GetVolatilityStop|| +
+|**OSCILLATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|RSI - Relative Strength Index|`Rsi`|GetRsi|| +|RSX - Jurik Trend Strength Index|`Rsx`||| +|AC - Acceleration Oscillator||||| +|AO - Awesome Oscillator||GetAwesome||| +|AROON - Aroon oscillator||GetAroon|Aroon|| +|BOP - Balance of Power||GetBop|Bop|| +|CCI - Commodity Channel Index||GetCci|Cci|| +|CFO - Chande Forcast Oscillator||||| +|CMO - Chande Momentum Oscillator||GetCmo|Cmo|| +|CHOP - Choppiness Index||GetChop||| +|COG - Ehler's Center of Gravity||||| +|COPPOCK - Coppock Curve||||| +|CRSI - Connor RSI||GetConnorsRsi||| +|CTI - Ehler's Correlation Trend Indicator||||| +|DOSC - Derivative Oscillator||||| +|EFI - Elder Ray's Force Index||GetElderRay||| +|FISHER - Fisher Transform||||| +|FOSC - Forecast Oscillator|||||| +|GATOR - Williams Alliator Oscillator||GetGator||| +|KDJ - KDJ Indicator (trend reversal)||||| +|KRI - Kairi Relative Index||||| +|RVGI - Relative Vigor Index||||| +|SMI - Stochastic Momentum Index||GetSmi||| +|SRSI - Stochastic RSI||GetStochRsi||| +|STC - Schaff Trend Cycle||GetStc||| +|STOCH - Stochastic Oscillator||`GetStoch||| +|TSI - True Strength Index||GetTsi||| +|UO - Ultimate Oscillator||GetUltimate||| +|WILLR - Larry Williams' %R||GetWilliamsR||| +
+|**VOLUME INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|ADL - Chaikin Accumulation Distribution Line||GetAdl|Ad|| +|ADOSC - Chaikin Accumulation Distribution Oscillator||GetChaikinOsc|AdOsc|| +|AOBV - Archer On-Balance Volume||||| +|CMF - Chaikin Money Flow||GetCmf||| +|EOM - Ease of Movement||||| +|KVO - Klinger Volume Oscillator||GetKvo|||| +|MFI - Money Flow Index||GetMfi||| +|NVI - Negative Volume Index||||| +|OBV - On-Balance Volume||GetObv||| +|PVI - Positive Volume Index||||| +|PVOL - Price-Volume||||| +|PVO - Percentage Volume Oscillator||GetPvo||| +|PVR - Price Volume Rank||||| +|PVT - Price Volume Trend||||| +|TVI - Trade Volume Index||||| +|VP - Volume Profile||||| +|VWAP - Volume Weighted Average Price||GetVwap||| +|VWMA - Volume Weighted Moving Average||GetVwma|||| +
+|**NUMERICAL ANALYSIS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|BETA - Beta coefficient||||| +|CORR - Correlation Coefficient||||| +|CURVATURE - Rate of Change in Direction or Slope|`Curvature`|||| +|ENTROPY - Measure of Uncertainty or Disorder|`Entropy`|||| +|KURTOSIS - Measure of Tails/Peakedness|`Kurtosis`|||| +|HUBER - Huber Loss|`Huberloss`|||| +|HURST - Hurst Exponent||GetHurst||| +|MAX - Maximum with exponential decay|`Max`|||| +|MEDIAN - Middle value|`Median`|||| +|MIN - Minimum with exponential decay|`Min`|||| +|MODE - Most Frequent Value|`Mode`|||| +|PERCENTILE - Rank Order|`Percentile`|||| +|RSQUARED - Coefficient of Determination R-Squared||||| +|SKEW - Skewness, asymmetry of distribution|`Skew`|||| +|SLOPE - Rate of Change, Linear Regression|`Slope`|||| +|STDDEV - Standard Deviation, Measure of Spread|`Stddev`|||| +|THEIL - Theil's U Statistics||||| +|TSF - Time Series Forecast|||`✔️`|`✔️`| +|VARIANCE - Average of Squared Deviations|`Variance`|||| +|ZSCORE - Standardized Score|`Zscore`|||| +
+|**ERRORS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|MAE - Mean Absolute Error|`Mae`|||| +|MAPD - Mean Absolute Percentage Deviation|`Mapd`|||| +|MAPE - Mean Absolute Percentage Error|`Mape`|||| +|MASE - Mean Absolute Scaled Error|`Mase`|||| +|MDA - Mean Directional Accuracy||||| +|ME - Mean Error|`Me`|||| +|MPE - Pean Percentage Error|`Mpe`|||| +|MSE - Mean Squared Error|`Mse`|||| +|MSLE - Mean Squared Logarithmic Error|`Msle`|||| +|RAE - Relative Absolute Error|`Rae`|||| +|RMSE - Root Mean Squared Error|`Rmse`|||| +|RSE - Relateive Squared Error|`Rse`|||| +|RMSLE - Root Mean Squared Logarithmic Error|`Rmsle`|||| +|SMAPE - Symmetric Mean Absolute Percentage Error|`Smape`|||| +
+|**AVERAGES & TRENDS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|AFIRMA - Autoregressive Finite Impulse Response Moving Average|`Afirma`|||| +|ALMA - Arnaud Legoux Moving Average|`Alma`|`✔️`|| +|DEMA - Double EMA Average|`Dema`|`✔️`|`✔️`| |DSMA - Deviation Scaled Moving Average|`Dsma`|||| |DWMA - Double WMA Average|`Dwma`|||| -|⭐EMA - Exponential Moving Average|`Ema`|`⭐`|`⭐`|`⭐`|`⭐`| +|EMA - Exponential Moving Average|`Ema`|`⭐`|`⭐`|`⭐`|`⭐`| |EPMA - Endpoint Moving Average|`Epma`|`✔️`||| |FRAMA - Fractal Adaptive Moving Average|`Frama`|||| |FWMA - Fibonacci Weighted Moving Average|`Fwma`|||| @@ -29,112 +151,34 @@ |HMA - Hull Moving Average|`Hma`|`✔️`||`✔️`| |HWMA - Holt-Winter Moving Average|`Hwma`|||| |JMA - Jurik Moving Average|`Jma`|||| +|JORDAN - Jordan Moving Average||||| |KAMA - Kaufman's Adaptive Moving Average|`Kama`|`✔️`|`✔️`|`✔️`| -|KDJ - KDJ Indicator (trend reversal)||||| |LTMA - Laguerre Transform Moving Average|`Ltma`|||| |MAAF - Median-Average Adaptive Filter|`Maaf`|||| -|MACD - Movign Average Convergence/Divergence||`✔️`|`✔️`|| |MAMA - MESA Adaptive Moving Average|`Mama`|`✔️`|`✔️`|| |MGDI - McGinley Dynamic Indicator|`Mgdi`|`✔️`||| +|MLMA - Minimal Lag Moving Average||||| |MMA - Modified Moving Average|`Mma`|||| |PPMA - Pivot Point Moving Average||||| -|PWMA - Pascal's Weighted Moving Average||||| +|PWMA - Pascal's Weighted Moving Average|`Pwma`|||| |QEMA - Quad Exponential Moving Average|`Qema`|||| |RMA - WildeR's Moving Average|`Rma`|||| |SINEMA - Sine Weighted Moving Average|`Sinema`|||| -|⭐SMA - Simple Moving Average|`Sma`|`⭐`|`⭐`|`⭐`|`⭐`| -|SMMA - Smoothed Moving Average|`Smma`|`✔️`||| -|SSF - Ehler's Super Smoother Filter||||| -|SUPERTREND - Supertrend||`✔️`||| -|SWMA - Symmetric Weighted Moving Average||||| -|T3 - Tillson T3 Moving Average|`T3`|`✔️`|`✔️`|| -|TEMA - Triple EMA Average|`Tema`|`✔️`|`✔️`|`✔️`| -|TRIMA - Triangular Moving Average|`Trima`|`✔️`||`✔️`| -|TSF - Time Series Forecast|||`✔️`|`✔️`| -|VIDYA - Variable Index Dynamic Average|`Vidya`|||`✔️`| -|VORTEX - Vortex Indicator||`✔️`||| -|WMA - Weighted Moving Average|`Wma`|`✔️`|`✔️`|`✔️`| -|ZLEMA - Zero Lag EMA Average|`Zlema`|||`✔️`| -|
|||| -|**VOLATILITY INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| -|ADL - Chaikin Accumulation Distribution Line||`GetAdl`|`Ad`|| -|ADOSC - Chaikin Accumulation Distribution Oscillator||`GetChaikinOsc`|`AdOsc`|| -|ATR - Average True Range||`GetAtr`|`Atr`|| -|ATRP - Average True Range Percent||||| -|ATRSTOP - ATR Trailing Stop ||`GetAtrStop`||| -|BETA - Beta coefficient||||| -|BBANDS - Bollinger Bands®||`BollingerBands`||| -|CHAND - Chandelier Exit||`GetChandelier`||| -|CRSI - Connor RSI||`GetConnorsRsi`||| -|CVI - Chaikins Volatility||||| -|DON - Donchian Channels||`GetDonchian`||| -|FCB - Fractal Chaos Bands||`GetFcb`||| -|FISHER - Fisher Transform||||| -|HV - Historical Volatility||||| -|ICH - Ichimoku Cloud||`GetIchimoku`||| -|KEL - Keltner Channels||`GetKeltner`||| -|NATR - Normalized Average True Range||`GetAtr`||| -|CHN - Price Channel Indicator||||| -|RSI - Relative Strength Index||`GetRsi`||| -|SAR - Parabolic Stop and Reverse||`GetParabolicSar`||| -|SRSI - Stochastic RSI||`GetStochRsi`||| -|STARC - Starc Bands||`GetStarcBands`||| -|TR - True Range||||| -|UI - Ulcer Index||`GetUlcerIndex`||| -|VSTOP - Volatility Stop||`GetVolatilityStop`||| -|
|||| -|**MOMENTUM INDICATORS & OSCILLATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| -|AC - Acceleration Oscillator||||| -|ADX - Average Directional Movement Index||`GetAdx`|`Adx`|| -|ADXR - Average Directional Movement Index|| `Rating`|`Adxr`|| -|AO - Awesome Oscillator||`GetAwesome`||| -|APO - Absolute Price Oscillator||`Apo`||| -|AROON - Aroon oscillator||`GetAroon`|`Aroon`|| -|BOP - Balance of Power||`GetBop`|`Bop`|| -|CCI - Commodity Channel Index||`GetCci`|`Cci`|| -|CFO - Chande Forcast Oscillator||||| -|CMO - Chande Momentum Oscillator||`GetCmo`|`Cmo`|| -|CHOP - Choppiness Index||`GetChop`||| -|COG - Center of Gravity||||| -|COPPOCK - Coppock Curve||||| -|CTI - Ehler's Correlation Trend Indicator||||| -|DPO - Detrended Price Oscillator||`GetDpo`||| -|DMI - Directional Movement Index||`GetDmi`||| -|EFI - Elder Ray's Force Index||`GetElderRay`||| -|FOSC - Forecast oscillator|||||| -|GATOR - Gator oscillator||`GetGator`||| -|HURST - Hurst Exponent||`GetHurst`||| -|KRI - Kairi Relative Index||||| -|KVO - Klinger Volume Oscillator||`GetKvo`|||| -|MFI - Money Flow Index||`GetMfi`||| -|MOM - Momentum||||| -|NVI - Negative Volume Index||||| -|PO - Price Oscillator||||| -|PPO - Percentage Price Oscillator||||| -|PMO - Price Momentum Oscillator||`GetPmo`||| -|PVI - Positive Volume Index||||| -|ROC - Rate of Change||GetRoc||| -|RVGI - Relative Vigor Index||||| -|SMI - Stochastic Momentum Index||`GetSmi`||| -|STC - Schaff Trend Cycle||`GetStc`||| -|STOCH - Stochastic Oscillator||`GetStoch`||| -|TRIX - 1-day ROC of TEMA||`GetTrix`||`trix.Run`| -|TSI - True Strength Index||`GetTsi`||| -|UO - Ultimate Oscillator||`GetUltimate`||| -|WILLR - Larry Williams' %R||GetWillia`msR`||| -|WGAT - Williams Alligator||`GetAlligator`||| -|
|||| -|**VOLUME INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| -|AOBV - Archer On-Balance Volume||||| -|CMF - Chaikin Money Flow||`GetCmf`||| -|EOM - Ease of Movement||||| -|KVO - Klinger Volume Oscilaltor||||| -|OBV - On-Balance Volume||`GetObv`||| -|PRS - Price Relative Strength||`GetPrs`||| -|PVOL - Price-Volume||||| -|PVO - Percentage Volume Oscillator||`GetPvo`||| -|PVR - Price Volume Rank||||| -|PVT - Price Volume Trend||||| -|VP - Volume Profile||||| -|VWAP - Volume Weighted Average Price||`GetVwap`||| -|VWMA - Volume Weighted Moving Average||`GetVwma`|||| +|SMA - Simple Moving Average|`Sma`||| +|SMMA - Smoothed Moving Average|`Smma`|`✔️`|| +|SSF - Ehler's Super Smoother Filter|||| +|SUPERTREND - Supertrend||`✔️`|| +|T3 - Tillson T3 Moving Average|`T3`|`✔️`|`✔️`| +|TEMA - Triple EMA Average|`Tema`|`✔️`|`✔️`| +|TRIMA - Triangular Moving Average|`Trima`|`✔️`|| +|VIDYA - Variable Index Dynamic Average|`Vidya`||| +|WMA - Weighted Moving Average|`Wma`|`✔️`|| +|ZLEMA - Zero Lag EMA Average|`Zlema`||| +
+|**BASIC TRANSFORMS**|**QuanTALib**|Skender.Stock|TALib.NETCore| +|OC2 - Midpoint price|️`.OC2`|CandlePart.OC2|MidPoint| +|HL2 - Median Price|️`.HL2`|CandlePart.HL2|MedPrice| +|HLC3 - Typical Price|️`.HLC3`|CandlePart.HLC3|TypPrice| +|OHL3 - Mean Price|`️.OHL3`|CandlePart.OHL3| +|OHLC4 - Average Price|`️.OHLC4`|CandlePart.OHLC4|AvgPrice| +|HLCC4 - Weighted Price|`️.HLCC4`||WclPrice| \ No newline at end of file diff --git a/docs/indicators/volatility/rvi/calc.md b/docs/indicators/volatility/rvi/calc.md new file mode 100644 index 00000000..eeb283a4 --- /dev/null +++ b/docs/indicators/volatility/rvi/calc.md @@ -0,0 +1,50 @@ +# The Math Behind RVI + +## Components of RVI + +The **Relative Volatility Index (RVI)** measures the direction of volatility in the market, using components like: + +- Standard deviation of price changes +- Simple moving average (SMA) to smooth volatility +- Separation of up and down price movements + +### RVI Formula + +The RVI is calculated using the following formula: + +$$ +\text{RVI}_t = 100 \times \frac{\text{SMA}(\sigma_{\text{up}}, N)}{\text{SMA}(\sigma_{\text{up}}, N) + \text{SMA}(\sigma_{\text{down}}, N)} +$$ + +Where: +- \( \text{RVI}_t \) is the RVI value at time \( t \) +- \( \sigma_{\text{up}} \) is the standard deviation of up moves over the lookback period \( N \) +- \( \sigma_{\text{down}} \) is the standard deviation of down moves over the lookback period \( N \) +- \( \text{SMA} \) represents the simple moving average applied over \( N \) periods + +### Up and Down Move Calculation + +The standard deviations \( \sigma_{\text{up}} \) and \( \sigma_{\text{down}} \) are calculated based on the price changes: + +$$ +\Delta \text{Price} = \text{Close}_t - \text{Close}_{t-1} +$$ + +- If \( \Delta \text{Price} > 0 \), it contributes to \( \sigma_{\text{up}} \) +- If \( \Delta \text{Price} < 0 \), it contributes to \( \sigma_{\text{down}} \) + +### Parameter Definitions + +RVI uses the following main parameters: + +- **Lookback period** (\( N \)): The number of periods used to calculate the standard deviations and SMAs. A typical value is 14. +- **Smoothing with SMA**: The standard deviations of up and down moves are smoothed using a simple moving average (SMA), making the RVI less sensitive to short-term fluctuations. + +### Computational Process + +For each new data point: +- Calculate the price change (\( \Delta \text{Price} \)) from the previous period. +- Separate the price changes into up moves and down moves. +- Compute the standard deviations (\( \sigma_{\text{up}} \) and \( \sigma_{\text{down}} \)) over the last \( N \) periods. +- Apply the simple moving average (SMA) to both up and down standard deviations. +- Use the RVI formula to produce the final RVI value. diff --git a/docs/indicators/volatility/rvi/rvi.md b/docs/indicators/volatility/rvi/rvi.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/readme.md b/docs/readme.md index 6364377f..f3ee745e 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,7 +1,3 @@ -# QuanTAlib - quantitative technical indicators for Quantower - -### (and other C#-based trading platorms) - [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=mihakralj_QuanTAlib&metric=ncloc)](https://sonarcloud.io/summary/overall?id=mihakralj_QuanTAlib) [![Codacy grade](https://img.shields.io/codacy/grade/b1f9109222234c87bce45f1fd4c63aee?style=flat-square)](https://app.codacy.com/gh/mihakralj/QuanTAlib/dashboard) [![codecov](https://codecov.io/gh/mihakralj/QuanTAlib/branch/main/graph/badge.svg?style=flat-square&token=YNMJRGKMTJ?style=flat-square)](https://codecov.io/gh/mihakralj/QuanTAlib) @@ -14,29 +10,33 @@ [![GitHub watchers](https://img.shields.io/github/watchers/mihakralj/QuanTAlib?style=flat-square)](https://github.com/mihakralj/QuanTAlib/watchers) [![.NET8.0](https://img.shields.io/badge/.NET-8.0-blue?style=flat-square)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) +![Alt text](./img/quotes.gif) + +# QuanTAlib - quantitative technical indicators for Quantower + **Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for analyzing quotes with [Quantower](https://www.quantower.com/) and other C#-based trading platforms. -**QuanTAlib** is written with some specific design criteria in mind - why there is '_yet another C# TA library_': +[**Visit documentation pages**](https://mihakralj.github.io/QuanTAlib/#/)
+[**List of indicators - implemented and planned**](indicators/indicators.md) -- Prioritize **real-time data analysis**: As new data items arrives, indicators don't have to re-calculate the entire history and can generate a result directly from the last item +**QuanTAlib** is a C# library written with some specific design criteria in mind. Here is why there is '_yet another C# TA library_': + +- QuanTAlib focuses on **[real-time data analysis](essays/realtime.md)**: As new data items arrives, indicators don't have to re-calculate the entire history and can generate a result directly from the last item - **Allow updates/corrections** of the last quote - QuanTAlib is re-calculating the last value as many times as required before continuing to the new bar - **Calculate early data right** - calculated data is as valid as mathematically possible from the first value onwards - no blackout or warming-up periods. All indicators return data from the first bar, alongside with a flag `isHot` - defining if calculation is already stable. ## Installation to Quantower -- `` is the directory where Quantower is installed - where `Start.lnk` launcher is +- `` is the directory where Quantower is installed - where `Start.lnk` launcher is. Copy any or all `dll` files as below: - Copy `Averages.dll` from Releases to `\Settings\Scripts\Indicators\Averages\Averages.dll` - Copy `Statistics.dll` from Releases to `\Settings\Scripts\Indicators\Statistics\Statistics.dll` +- Copy `Volatility.dll` from Releases to `\Settings\Scripts\Indicators\Volatility\Volatility.dll` - Copy `SyntheticVendor.dll` from Releases to `\Settings\Scripts\Vendors\SyntheticVendor\SyntheticVendor.dll` -![Alt text](./img/quotes.gif) + QuanTAlib is intended for developers and users of Quantower, therefore it does not focus on privind sources of OHLCV quotes. There are some very basic data feeds available to use in the learning process: `GBM_Feed` for Random (Geometric Brownian Motion) data, and `SyntheticVendor` data generator for Quantower. -### Coverage - -[List of indicators - implemented and planned](indicators/indicators.md) - ### Validation QuanTAlib uses validation tests with four other TA libraries to assure accuracy and validity of results: diff --git a/docs/styles.css b/docs/styles.css new file mode 100644 index 00000000..5f30ab96 --- /dev/null +++ b/docs/styles.css @@ -0,0 +1,1617 @@ +:root { + /* Base Styles */ + box-sizing: border-box; + background-color: var(--base-background-color); + font-size: var(--base-font-size); + font-weight: var(--base-font-weight); + line-height: var(--base-line-height); + letter-spacing: var(--base-letter-spacing); + color: var(--base-color); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Base Colors */ + --mono-hue: 201; + --mono-saturation: 18%; + --mono-shade3: hsl(var(--mono-hue), var(--mono-saturation), 13%); + --mono-shade2: hsl(var(--mono-hue), var(--mono-saturation), 15%); + --mono-shade1: hsl(var(--mono-hue), var(--mono-saturation), 17%); + --mono-base: hsl(var(--mono-hue), var(--mono-saturation), 19%); + --mono-tint1: hsl(var(--mono-hue), var(--mono-saturation), 25%); + --mono-tint2: hsl(var(--mono-hue), var(--mono-saturation), 35%); + --mono-tint3: hsl(var(--mono-hue), var(--mono-saturation), 43%); + + /* Theme Colors */ + --theme-hue: 204; + --theme-saturation: 90%; + --theme-lightness: 45%; + --theme-color: hsl(var(--theme-hue), var(--theme-saturation), var(--theme-lightness)); + + /* Base Variables */ + --base-background-color: var(--mono-base); + --base-color: #d3d3d3; + --base-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + --base-font-size: 14px; + --base-font-weight: normal; + --base-line-height: 1.7; + + /* Modular Scale */ + --modular-scale: 1.0; + --modular-scale--2: calc(var(--modular-scale--1) / var(--modular-scale)); + --modular-scale--1: calc(var(--modular-scale-1) / var(--modular-scale)); + --modular-scale-1: 1rem; + --modular-scale-2: calc(var(--modular-scale-1) * var(--modular-scale)); + --modular-scale-3: calc(var(--modular-scale-2) * var(--modular-scale)); + --modular-scale-4: calc(var(--modular-scale-3) * var(--modular-scale)); + --modular-scale-5: calc(var(--modular-scale-4) * var(--modular-scale)); + + /* Font Sizes */ + --font-size-xxxl: var(--modular-scale-5); + --font-size-xxl: var(--modular-scale-4); + --font-size-xl: var(--modular-scale-3); + --font-size-l: var(--modular-scale-2); + --font-size-m: var(--modular-scale-1); + --font-size-s: var(--modular-scale--1); + --font-size-xs: var(--modular-scale--2); + + /* Headings */ + --heading-color: #fff; + --heading-font-weight: var(--strong-font-weight); + --heading-margin: 2.5rem 0 0; + --heading-h1-font-size: var(--font-size-xxl); + --heading-h2-font-size: var(--font-size-xl); + --heading-h3-font-size: var(--font-size-l); + --heading-h4-font-size: var(--font-size-m); + --heading-h5-font-size: var(--font-size-s); + --heading-h6-font-size: var(--font-size-xs); + + /* Blockquote */ + --blockquote-background: var(--mono-shade2); + --blockquote-border-color: var(--theme-color); + --blockquote-border-style: solid; + --blockquote-border-width: 0 0 0 4px; + --blockquote-border-radius: 0 var(--border-radius-m) var(--border-radius-m) 0; + --blockquote-padding: 1.5em; + + /* Code */ + --code-font-family: Inconsolata, Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; + --code-font-size: calc(var(--font-size-m) * 0.95); + --code-font-weight: normal; + --code-tab-size: 4; + --code-block-border-radius: var(--border-radius-m); + --code-block-padding: 1.75em 1.5em 1.5em 1.5em; + --code-inline-background: var(--mono-tint1); + --code-inline-border-radius: var(--border-radius-s); + --code-inline-padding: 0.125em 0.4em; + + /* Notice */ + --notice-background: var(--mono-shade2); + --notice-border-radius: 0 var(--border-radius-m) var(--border-radius-m) 0; + --notice-border-width: 0 0 0 4px; + --notice-padding: 1em 1.5em 1em 3em; + + /* Table */ + --table-cell-border-color: var(--mono-tint1); + --table-cell-border-width: 1px 0; + --table-cell-padding: 0.75em 0.5em; + --table-row-odd-background: var(--mono-shade2); + + /* Layout */ + --content-max-width: 75em; + + /* Cover */ + --cover-margin: 0 auto; + --cover-max-width: 40em; + --cover-background-color: var(--base-background-color); + --cover-background-image: radial-gradient(ellipse at center bottom, var(--mono-tint3), transparent); + + /* Navbar */ + --navbar-menu-background: var(--mono-tint1); + --navbar-menu-border-radius: var(--border-radius-m); + --navbar-menu-padding: 0.5em; + + /* Sidebar */ + --sidebar-background: var(--mono-shade1); + --sidebar-border-color: var(--mono-tint1); + --sidebar-border-width: 0 1px 0 0; + --sidebar-padding: 0 25px; + --sidebar-width: 17rem; + + /* Search */ + --search-margin: 1.5rem 0 0; + --search-input-background-color: var(--mono-shade2); + --search-input-border-color: var(--mono-tint1); + --search-input-padding: 0.5em; + + /* Misc UI */ + --border-radius-s: 2px; + --border-radius-m: 4px; + --duration-fast: 0.25s; + --duration-medium: 0.5s; + --duration-slow: 1s; +} + +.github-corner{ + position:absolute; + z-index:40; + top:0; + right:0; + border-bottom:0; + text-decoration:none +} +.github-corner svg{ + height:70px; + width:70px; + fill:var(--theme-color); + color:var(--base-background-color) +} +.github-corner:hover .octo-arm{ + -webkit-animation:octocat-wave 560ms ease-in-out; + animation:octocat-wave 560ms ease-in-out +} +@-webkit-keyframes octocat-wave{ + 0%,100%{ + transform:rotate(0) + } + 20%,60%{ + transform:rotate(-25deg) + } + 40%,80%{ + transform:rotate(10deg) + } +} +@keyframes octocat-wave{ + 0%,100%{ + transform:rotate(0) + } + 20%,60%{ + transform:rotate(-25deg) + } + 40%,80%{ + transform:rotate(10deg) + } +} +.progress{ + position:fixed; + z-index:2147483647; + top:0; + left:0; + right:0; + height:3px; + width:0; + background-color:var(--theme-color); + transition:width var(--duration-fast),opacity calc(var(--duration-fast)*2) +} +body.ready-transition:after,body.ready-transition>*:not(.progress){ + opacity:0; + transition:opacity var(--spinner-transition-duration) +} +body.ready-transition:after{ + content:""; + position:absolute; + z-index:1000; + top:calc(50% - var(--spinner-size)/2); + left:calc(50% - var(--spinner-size)/2); + height:var(--spinner-size); + width:var(--spinner-size); + border:var(--spinner-track-width, 0) solid var(--spinner-track-color); + border-left-color:var(--theme-color); + border-radius:50%; + -webkit-animation:spinner var(--duration-slow) infinite linear; + animation:spinner var(--duration-slow) infinite linear +} +body.ready-transition.ready-spinner:after{ + opacity:1 +} +body.ready-transition.ready-fix:after{ + opacity:0 +} +body.ready-transition.ready-fix>*:not(.progress){ + opacity:1; + transition-delay:var(--spinner-transition-duration) +} +@-webkit-keyframes spinner{ + 0%{ + transform:rotate(0deg) + } + 100%{ + transform:rotate(360deg) + } +} +@keyframes spinner{ + 0%{ + transform:rotate(0deg) + } + 100%{ + transform:rotate(360deg) + } +} +*,*:before,*:after{ + box-sizing:inherit; + font-size:inherit; + -webkit-overflow-scrolling:touch; + -webkit-tap-highlight-color:rgba(0,0,0,0); + -webkit-text-size-adjust:none; + -webkit-touch-callout:none +} +html,button,input,optgroup,select,textarea{ + font-family:var(--base-font-family) +} +button,input,optgroup,select,textarea{ + font-size:100%; + margin:0 +} +a{ + text-decoration:none; + -webkit-text-decoration-skip:ink; + text-decoration-skip-ink:auto +} +body{ + margin:0 +} +hr{ + height:0; + margin:2em 0; + border:none; + border-bottom:var(--hr-border, 0) +} +img{ + max-width:100%; + border:0 +} +main{ + display:block; + position:relative; + overflow-x:hidden; + min-height:100vh +} +main.hidden{ + display:none +} +mark{ + background:var(--mark-background); + color:var(--mark-color) +} +pre{ + font-family:var(--pre-font-family); + font-size:var(--pre-font-size); + font-weight:var(--pre-font-weight); + line-height:var(--pre-line-height) +} +small{ + display:inline-block; + font-size:var(--small-font-size) +} +strong{ + font-weight:var(--strong-font-weight); + color:var(--strong-color, currentColor) +} +sub,sup{ + font-size:var(--subsup-font-size); + line-height:0; + position:relative; + vertical-align:baseline +} +sub{ + bottom:-0.25em +} +sup{ + top:-0.5em +} +body:not([data-platform^=Mac]) *{ + scrollbar-color:hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.3) hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.1); + scrollbar-width:thin +} +body:not([data-platform^=Mac]) * ::-webkit-scrollbar{ + width:5px; + height:5px +} +body:not([data-platform^=Mac]) * ::-webkit-scrollbar-thumb{ + background:hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.3) +} +body:not([data-platform^=Mac]) * ::-webkit-scrollbar-track{ + background:hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.1) +} +::-moz-selection{ + background: var(--selection-color, #0074d9); + color: #ffffff; +} +::selection{ + background: var(--selection-color, #0074d9); + color: #ffffff; +} +.emoji{ + height:var(--emoji-size); + vertical-align:middle +} +.task-list-item{ + list-style:none +} +.task-list-item input{ + margin-right:.5em; + margin-left:0; + vertical-align:.075em +} +.markdown-section code[class*=lang-],.markdown-section pre[data-lang]{ + font-family:var(--code-font-family); + font-size:var(--code-font-size); + font-weight:var(--code-font-weight); + letter-spacing:normal; + line-height:var(--code-block-line-height); + -moz-tab-size:var(--code-tab-size); + -o-tab-size:var(--code-tab-size); + tab-size:var(--code-tab-size); + text-align:left; + white-space:pre; + word-spacing:normal; + word-wrap:normal; + word-break:normal; + -webkit-hyphens:none; + hyphens:none +} +.markdown-section pre[data-lang]{ + position:relative; + overflow:hidden; + margin:var(--code-block-margin); + padding:0; + border-radius:var(--code-block-border-radius) +} +.markdown-section pre[data-lang]::after{ + content:attr(data-lang); + position:absolute; + top:.75em; + right:.75em; + opacity:.6; + color:inherit; + font-size:var(--font-size-s); + line-height:1 +} +.markdown-section pre[data-lang] code{ + display:block; + overflow:auto; + padding:var(--code-block-padding) +} +code[class*=lang-],pre[data-lang]{ + color:var(--code-theme-text) +} +pre[data-lang]::-moz-selection,pre[data-lang] ::-moz-selection,code[class*=lang-]::-moz-selection,code[class*=lang-] ::-moz-selection{ + background:var(--code-theme-selection, var(--selection-color)) +} +pre[data-lang]::selection,pre[data-lang] ::selection,code[class*=lang-]::selection,code[class*=lang-] ::selection{ + background:var(--code-theme-selection, var(--selection-color)) +} +:not(pre)>code[class*=lang-],pre[data-lang]{ + background:var(--code-theme-background) +} +.namespace{ + opacity:.7 +} +.token.comment,.token.prolog,.token.doctype,.token.cdata{ + color:var(--code-theme-comment) +} +.token.punctuation{ + color:var(--code-theme-punctuation) +} +.token.property,.token.tag,.token.boolean,.token.number,.token.constant,.token.symbol,.token.deleted{ + color:var(--code-theme-tag) +} +.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.inserted{ + color:var(--code-theme-selector) +} +.token.operator,.token.entity,.token.url,.language-css .token.string,.style .token.string{ + color:var(--code-theme-operator) +} +.token.atrule,.token.attr-value,.token.keyword{ + color:var(--code-theme-keyword) +} +.token.function{ + color:var(--code-theme-function) +} +.token.regex,.token.important,.token.variable{ + color:var(--code-theme-variable) +} +.token.important,.token.bold{ + font-weight:bold +} +.token.italic{ + font-style:italic +} +.token.entity{ + cursor:help +} +.markdown-section{ + position:relative; + max-width:var(--content-max-width); + margin:0 auto; + padding:2rem 45px +} +.app-nav:not(:empty)~main .markdown-section{ + padding-top:3.5rem +} +.markdown-section figure,.markdown-section p,.markdown-section ol,.markdown-section ul{ + margin:1em 0 +} +.markdown-section ol,.markdown-section ul{ + padding-left:1.5rem +} +.markdown-section ol ol,.markdown-section ol ul,.markdown-section ul ol,.markdown-section ul ul{ + margin-top:.15rem; + margin-bottom:.15rem +} +.markdown-section a{ + border-bottom:var(--link-border-bottom); + color:var(--link-color); + -webkit-text-decoration:var(--link-text-decoration); + text-decoration:var(--link-text-decoration); + -webkit-text-decoration-color:var(--link-text-decoration-color); + text-decoration-color:var(--link-text-decoration-color) +} +.markdown-section a:hover{ + border-bottom:var(--link-border-bottom--hover, var(--link-border-bottom, 0)); + color:var(--link-color--hover, var(--link-color)); + -webkit-text-decoration:var(--link-text-decoration--hover, var(--link-text-decoration)); + text-decoration:var(--link-text-decoration--hover, var(--link-text-decoration)); + -webkit-text-decoration-color:var(--link-text-decoration-color--hover, var(--link-text-decoration-color)); + text-decoration-color:var(--link-text-decoration-color--hover, var(--link-text-decoration-color)) +} +.markdown-section a.anchor{ + border-bottom:0; + color:inherit; + text-decoration:none +} +.markdown-section a.anchor:hover{ + text-decoration:underline +} +.markdown-section blockquote{ + overflow:visible; + margin:2em 0; + padding:var(--blockquote-padding); + border-width:var(--blockquote-border-width, 0); + border-style:var(--blockquote-border-style); + border-color:var(--blockquote-border-color); + border-radius:var(--blockquote-border-radius); + background:var(--blockquote-background); + color:var(--blockquote-color); + font-family:var(--blockquote-font-family); + font-size:var(--blockquote-font-size); + font-style:var(--blockquote-font-style); + font-weight:var(--blockquote-font-weight); + quotes:"“" "”" "‘" "’" +} +.markdown-section blockquote em{ + font-family:var(--blockquote-em-font-family); + font-size:var(--blockquote-em-font-size); + font-style:var(--blockquote-em-font-style); + font-weight:var(--blockquote-em-font-weight) +} +.markdown-section blockquote p:first-child{ + margin-top:0 +} +.markdown-section blockquote p:first-child:before,.markdown-section blockquote p:first-child:after{ + color:var(--blockquote-quotes-color); + font-family:var(--blockquote-quotes-font-family); + font-size:var(--blockquote-quotes-font-size); + line-height:0 +} +.markdown-section blockquote p:first-child:before{ + content:var(--blockquote-quotes-open); + margin-right:.15em; + vertical-align:-0.45em +} +.markdown-section blockquote p:first-child:after{ + content:var(--blockquote-quotes-close); + margin-left:.15em; + vertical-align:-0.55em +} +.markdown-section blockquote p:last-child{ + margin-bottom:0 +} +.markdown-section code{ + font-family:var(--code-font-family); + font-size:var(--code-font-size); + font-weight:var(--code-font-weight); + line-height:inherit +} +.markdown-section code:not([class*=lang-]):not([class*=language-]){ + margin:var(--code-inline-margin); + padding:var(--code-inline-padding); + border-radius:var(--code-inline-border-radius); + background:var(--code-inline-background); + color:var(--code-inline-color, currentColor); + white-space:nowrap +} +.markdown-section h1:first-child,.markdown-section h2:first-child,.markdown-section h3:first-child,.markdown-section h4:first-child,.markdown-section h5:first-child,.markdown-section h6:first-child{ + margin-top:0 +} +.markdown-section h1 a[data-id],.markdown-section h2 a[data-id],.markdown-section h3 a[data-id],.markdown-section h4 a[data-id],.markdown-section h5 a[data-id],.markdown-section h6 a[data-id]{ + display:inline-block +} +.markdown-section h1 code,.markdown-section h2 code,.markdown-section h3 code,.markdown-section h4 code,.markdown-section h5 code,.markdown-section h6 code{ + font-size:.875em +} +.markdown-section h1+h2,.markdown-section h1+h3,.markdown-section h1+h4,.markdown-section h1+h5,.markdown-section h1+h6,.markdown-section h2+h3,.markdown-section h2+h4,.markdown-section h2+h5,.markdown-section h2+h6,.markdown-section h3+h4,.markdown-section h3+h5,.markdown-section h3+h6,.markdown-section h4+h5,.markdown-section h4+h6,.markdown-section h5+h6{ + margin-top:1rem +} +.markdown-section h1{ + margin:var(--heading-h1-margin, var(--heading-margin)); + padding:var(--heading-h1-padding, var(--heading-padding)); + border-width:var(--heading-h1-border-width, 0); + border-style:var(--heading-h1-border-style); + border-color:var(--heading-h1-border-color); + font-family:var(--heading-h1-font-family, var(--heading-font-family)); + font-size:var(--heading-h1-font-size); + font-weight:var(--heading-h1-font-weight, var(--heading-font-weight)); + line-height:var(--base-line-height); + color:var(--heading-h1-color, var(--heading-color)) +} +.markdown-section h2{ + margin:var(--heading-h2-margin, var(--heading-margin)); + padding:var(--heading-h2-padding, var(--heading-padding)); + border-width:var(--heading-h2-border-width, 0); + border-style:var(--heading-h2-border-style); + border-color:var(--heading-h2-border-color); + font-family:var(--heading-h2-font-family, var(--heading-font-family)); + font-size:var(--heading-h2-font-size); + font-weight:var(--heading-h2-font-weight, var(--heading-font-weight)); + line-height:var(--base-line-height); + color:var(--heading-h2-color, var(--heading-color)) +} +.markdown-section h3{ + margin:var(--heading-h3-margin, var(--heading-margin)); + padding:var(--heading-h3-padding, var(--heading-padding)); + border-width:var(--heading-h3-border-width, 0); + border-style:var(--heading-h3-border-style); + border-color:var(--heading-h3-border-color); + font-family:var(--heading-h3-font-family, var(--heading-font-family)); + font-size:var(--heading-h3-font-size); + font-weight:var(--heading-h3-font-weight, var(--heading-font-weight)); + color:var(--heading-h3-color, var(--heading-color)) +} +.markdown-section h4{ + margin:var(--heading-h4-margin, var(--heading-margin)); + padding:var(--heading-h4-padding, var(--heading-padding)); + border-width:var(--heading-h4-border-width, 0); + border-style:var(--heading-h4-border-style); + border-color:var(--heading-h4-border-color); + font-family:var(--heading-h4-font-family, var(--heading-font-family)); + font-size:var(--heading-h4-font-size); + font-weight:var(--heading-h4-font-weight, var(--heading-font-weight)); + color:var(--heading-h4-color, var(--heading-color)) +} +.markdown-section h5{ + margin:var(--heading-h5-margin, var(--heading-margin)); + padding:var(--heading-h5-padding, var(--heading-padding)); + border-width:var(--heading-h5-border-width, 0); + border-style:var(--heading-h5-border-style); + border-color:var(--heading-h5-border-color); + font-family:var(--heading-h5-font-family, var(--heading-font-family)); + font-size:var(--heading-h5-font-size); + font-weight:var(--heading-h5-font-weight, var(--heading-font-weight)); + color:var(--heading-h5-color, var(--heading-color)) +} +.markdown-section h6{ + margin:var(--heading-h6-margin, var(--heading-margin)); + padding:var(--heading-h6-padding, var(--heading-padding)); + border-width:var(--heading-h6-border-width, 0); + border-style:var(--heading-h6-border-style); + border-color:var(--heading-h6-border-color); + font-family:var(--heading-h6-font-family, var(--heading-font-family)); + font-size:var(--heading-h6-font-size); + font-weight:var(--heading-h6-font-weight, var(--heading-font-weight)); + color:var(--heading-h6-color, var(--heading-color)) +} +.markdown-section iframe{ + margin:1em 0 +} +.markdown-section img{ + max-width:100% +} +.markdown-section kbd{ + display:inline-block; + min-width:var(--kbd-min-width); + margin:var(--kbd-margin); + padding:var(--kbd-padding); + border:var(--kbd-border); + border-radius:var(--kbd-border-radius); + background:var(--kbd-background); + font-family:inherit; + font-size:var(--kbd-font-size); + text-align:center; + letter-spacing:0; + line-height:1; + color:var(--kbd-color) +} +.markdown-section kbd+kbd{ + margin-left:-0.15em +} +.markdown-section table{ + display:block; + overflow:auto; + margin:1rem 0; + border-spacing:0; + border-collapse:collapse +} +.markdown-section th:not([align]){ + text-align:left +} +.markdown-section thead{ + border-color:var(--table-head-border-color); + border-style:solid; + border-width:var(--table-head-border-width, 0); + background:var(--table-head-background) +} +.markdown-section th{ + font-weight:var(--table-head-font-weight, 700); + color:var(--strong-color); +} +.markdown-section td{ + border-color:var(--table-cell-border-color); + border-style:solid; + border-width:var(--table-cell-border-width, 0) +} +.markdown-section td, +.markdown-section th { + padding:var(--table-cell-padding); + line-height: 1.1; /* Reduced from default 1.7 */ +} +.markdown-section th strong, +.markdown-section th b, +.markdown-section th em, +.markdown-section td strong, +.markdown-section td b, +.markdown-section td em { + font-size: 1.2em; + font-weight: var(--table-head-font-weight, 700); + color: var(--strong-color); + line-height: 2.0; +} +.markdown-section tbody{ + border-color:var(--table-body-border-color); + border-style:solid; + border-width:var(--table-body-border-width, 0) +} +.markdown-section tbody tr:nth-child(odd){ + background:var(--table-row-odd-background) +} +.markdown-section tbody tr:nth-child(even){ + background:var(--table-row-even-background) +} +.markdown-section>ul .task-list-item{ + margin-left:-1.25em +} +.markdown-section>ul .task-list-item .task-list-item{ + margin-left:0 +} +.markdown-section .table-wrapper{ + overflow-x:auto +} +.markdown-section .table-wrapper table{ + display:table; + width:100% +} +.markdown-section .table-wrapper td::before{ + display:none +} +@media(max-width: 30em){ + .markdown-section .table-wrapper tbody,.markdown-section .table-wrapper tr,.markdown-section .table-wrapper td{ + display:block + } + .markdown-section .table-wrapper th,.markdown-section .table-wrapper td{ + border:none + } + .markdown-section .table-wrapper thead{ + display:none + } + .markdown-section .table-wrapper tr{ + border-color:var(--table-cell-border-color); + border-style:solid; + border-width:var(--table-cell-border-width, 0); + padding:var(--table-cell-padding) + } + .markdown-section .table-wrapper tr:not(:last-child){ + border-bottom:0 + } + .markdown-section .table-wrapper td{ + padding:.15em 0 .15em 8em + } + .markdown-section .table-wrapper td::before{ + display:inline-block; + width:8em; + margin-left:-8em; + font-weight:bold; + text-align:left + } +} +.markdown-section .tip,.markdown-section .warn{ + position:relative; + margin:2em 0; + padding:var(--notice-padding); + border-width:var(--notice-border-width, 0); + border-style:var(--notice-border-style); + border-color:var(--notice-border-color); + border-radius:var(--notice-border-radius); + background:var(--notice-background); + font-family:var(--notice-font-family); + font-weight:var(--notice-font-weight); + color:var(--notice-color) +} +.markdown-section .tip:before,.markdown-section .warn:before{ + display:inline-block; + position:var(--notice-before-position, relative); + top:var(--notice-before-top); + left:var(--notice-before-left); + height:var(--notice-before-height); + width:var(--notice-before-width); + margin:var(--notice-before-margin); + padding:var(--notice-before-padding); + border-radius:var(--notice-before-border-radius); + line-height:var(--notice-before-line-height); + font-family:var(--notice-before-font-family); + font-size:var(--notice-before-font-size); + font-weight:var(--notice-before-font-weight); + text-align:center +} +.markdown-section .tip{ + border-width:var(--notice-important-border-width, var(--notice-border-width, 0)); + border-style:var(--notice-important-border-style, var(--notice-border-style)); + border-color:var(--notice-important-border-color, var(--notice-border-color)); + background:var(--notice-important-background, var(--notice-background)); + color:var(--notice-important-color, var(--notice-color)) +} +.markdown-section .tip:before{ + content:var(--notice-important-before-content, var(--notice-before-content)); + background:var(--notice-important-before-background, var(--notice-before-background)); + color:var(--notice-important-before-color, var(--notice-before-color)) +} +.markdown-section .warn{ + border-width:var(--notice-tip-border-width, var(--notice-border-width, 0)); + border-style:var(--notice-tip-border-style, var(--notice-border-style)); + border-color:var(--notice-tip-border-color, var(--notice-border-color)); + background:var(--notice-tip-background, var(--notice-background)); + color:var(--notice-tip-color, var(--notice-color)) +} +.markdown-section .warn:before{ + content:var(--notice-tip-before-content, var(--notice-before-content)); + background:var(--notice-tip-before-background, var(--notice-before-background)); + color:var(--notice-tip-before-color, var(--notice-before-color)) +} +.cover{ + display:none; + position:relative; + z-index:20; + min-height:100vh; + flex-direction:column; + align-items:center; + justify-content:center; + padding:calc(var(--cover-border-inset, 0px) + var(--cover-border-width, 0px)); + color:var(--cover-color); + text-align:var(--cover-text-align) +} +@media screen and (-ms-high-contrast: active),screen and (-ms-high-contrast: none){ + .cover{ + height:100vh + } +} +.cover:before,.cover:after{ + content:""; + position:absolute +} +.cover:before{ + top:0; + bottom:0; + left:0; + right:0; + background-blend-mode:var(--cover-background-blend-mode); + background-color:var(--cover-background-color); + background-image:var(--cover-background-image); + background-position:var(--cover-background-position); + background-repeat:var(--cover-background-repeat); + background-size:var(--cover-background-size) +} +.cover:after{ + top:var(--cover-border-inset, 0); + bottom:var(--cover-border-inset, 0); + left:var(--cover-border-inset, 0); + right:var(--cover-border-inset, 0); + border-width:var(--cover-border-width, 0); + border-style:solid; + border-color:var(--cover-border-color) +} +.cover a{ + border-bottom:var(--cover-link-border-bottom); + color:var(--cover-link-color); + -webkit-text-decoration:var(--cover-link-text-decoration); + text-decoration:var(--cover-link-text-decoration); + -webkit-text-decoration-color:var(--cover-link-text-decoration-color); + text-decoration-color:var(--cover-link-text-decoration-color) +} +.cover a:hover{ + border-bottom:var(--cover-link-border-bottom--hover, var(--cover-link-border-bottom)); + color:var(--cover-link-color--hover, var(--cover-link-color)); + -webkit-text-decoration:var(--cover-link-text-decoration--hover, var(--cover-link-text-decoration)); + text-decoration:var(--cover-link-text-decoration--hover, var(--cover-link-text-decoration)); + -webkit-text-decoration-color:var(--cover-link-text-decoration-color--hover, var(--cover-link-text-decoration-color)); + text-decoration-color:var(--cover-link-text-decoration-color--hover, var(--cover-link-text-decoration-color)) +} +.cover h1{ + color:var(--cover-heading-color); + position:relative; + margin:0; + font-size:var(--cover-heading-font-size); + font-weight:var(--cover-heading-font-weight); + line-height:1.2 +} +.cover h1 a,.cover h1 a:hover{ + display:block; + border-bottom:none; + color:inherit; + text-decoration:none +} +.cover h1 small{ + position:absolute; + bottom:0; + margin-left:.5em +} +.cover h1 span{ + font-size:calc(var(--cover-heading-font-size-min)*1px) +} +@media(min-width: 26em){ + .cover h1 span{ + font-size:calc(var(--cover-heading-font-size-min)*1px + (var(--cover-heading-font-size-max) - var(--cover-heading-font-size-min))*(100vw - 420px)/604) + } +} +@media(min-width: 64em){ + .cover h1 span{ + font-size:calc(var(--cover-heading-font-size-max)*1px) + } +} +.cover blockquote{ + margin:0; + color:var(--cover-blockquote-color); + font-size:var(--cover-blockquote-font-size) +} +.cover blockquote a{ + color:inherit +} +.cover ul{ + padding:0; + list-style-type:none +} +.cover .cover-main{ + position:relative; + z-index:1; + max-width:var(--cover-max-width); + margin:var(--cover-margin); + padding:0 45px +} +.cover .cover-main>p:last-child{ + margin:1.25em -0.25em +} +.cover .cover-main>p:last-child a{ + display:block; + margin:.375em .25em; + padding:var(--cover-button-padding); + border:var(--cover-button-border); + border-radius:var(--cover-button-border-radius); + box-shadow:var(--cover-button-box-shadow); + background:var(--cover-button-background); + text-align:center; + -webkit-text-decoration:var(--cover-button-text-decoration); + text-decoration:var(--cover-button-text-decoration); + -webkit-text-decoration-color:var(--cover-button-text-decoration-color); + text-decoration-color:var(--cover-button-text-decoration-color); + color:var(--cover-button-color); + white-space:nowrap; + transition:var(--cover-button-transition) +} +.cover .cover-main>p:last-child a:hover{ + border:var(--cover-button-border--hover, var(--cover-button-border)); + box-shadow:var(--cover-button-box-shadow--hover, var(--cover-button-box-shadow)); + background:var(--cover-button-background--hover, var(--cover-button-background)); + -webkit-text-decoration:var(--cover-button-text-decoration--hover, var(--cover-button-text-decoration)); + text-decoration:var(--cover-button-text-decoration--hover, var(--cover-button-text-decoration)); + -webkit-text-decoration-color:var(--cover-button-text-decoration-color--hover, var(--cover-button-text-decoration-color)); + text-decoration-color:var(--cover-button-text-decoration-color--hover, var(--cover-button-text-decoration-color)); + color:var(--cover-button-color--hover, var(--cover-button-color)) +} +.cover .cover-main>p:last-child a:first-child{ + border:var(--cover-button-primary-border, var(--cover-button-border)); + box-shadow:var(--cover-button-primary-box-shadow, var(--cover-button-box-shadow)); + background:var(--cover-button-primary-background, var(--cover-button-background)); + -webkit-text-decoration:var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration)); + text-decoration:var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration)); + -webkit-text-decoration-color:var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color)); + text-decoration-color:var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color)); + color:var(--cover-button-primary-color, var(--cover-button-color)) +} +.cover .cover-main>p:last-child a:first-child:hover{ + border:var(--cover-button-primary-border--hover, var(--cover-button-border--hover, var(--cover-button-primary-border, var(--cover-button-border)))); + box-shadow:var(--cover-button-primary-box-shadow--hover, var(--cover-button-box-shadow--hover, var(--cover-button-primary-box-shadow, var(--cover-button-box-shadow)))); + background:var(--cover-button-primary-background--hover, var(--cover-button-background--hover, var(--cover-button-primary-background, var(--cover-button-background)))); + -webkit-text-decoration:var(--cover-button-primary-text-decoration--hover, var(--cover-button-text-decoration--hover, var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration)))); + text-decoration:var(--cover-button-primary-text-decoration--hover, var(--cover-button-text-decoration--hover, var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration)))); + -webkit-text-decoration-color:var(--cover-button-primary-text-decoration-color--hover, var(--cover-button-text-decoration-color--hover, var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color)))); + text-decoration-color:var(--cover-button-primary-text-decoration-color--hover, var(--cover-button-text-decoration-color--hover, var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color)))); + color:var(--cover-button-primary-color--hover, var(--cover-button-color--hover, var(--cover-button-primary-color, var(--cover-button-color)))) +} +@media(min-width: 30.01em){ + .cover .cover-main>p:last-child a{ + display:inline-block + } +} +.cover .mask{ + visibility:var(--cover-background-mask-visibility, hidden); + position:absolute; + top:0; + bottom:0; + left:0; + right:0; + background-color:var(--cover-background-mask-color); + opacity:var(--cover-background-mask-opacity) +} +.cover.has-mask .mask{ + visibility:visible +} +.cover.show{ + display:flex +} +.app-nav{ + position:absolute; + z-index:30; + top:calc(35px - .5em*var(--base-line-height)); + left:45px; + right:80px; + text-align:right +} +.app-nav.no-badge{ + right:45px +} +.app-nav li>img,.app-nav li>a>img{ + margin-top:-0.25em; + vertical-align:middle +} +.app-nav li>img:first-child,.app-nav li>a>img:first-child{ + margin-right:.5em +} +.app-nav ul,.app-nav li{ + margin:0; + padding:0; + list-style:none +} +.app-nav li{ + position:relative +} +.app-nav li a{ + display:block; + line-height:1; + transition:var(--navbar-root-transition) +} +.app-nav>ul>li{ + display:inline-block; + margin:var(--navbar-root-margin) +} +.app-nav>ul>li:first-child{ + margin-left:0 +} +.app-nav>ul>li:last-child{ + margin-right:0 +} +.app-nav>ul>li>a,.app-nav>ul>li>span{ + padding:var(--navbar-root-padding); + border-width:var(--navbar-root-border-width, 0); + border-style:var(--navbar-root-border-style); + border-color:var(--navbar-root-border-color); + border-radius:var(--navbar-root-border-radius); + background:var(--navbar-root-background); + color:var(--navbar-root-color); + -webkit-text-decoration:var(--navbar-root-text-decoration); + text-decoration:var(--navbar-root-text-decoration); + -webkit-text-decoration-color:var(--navbar-root-text-decoration-color); + text-decoration-color:var(--navbar-root-text-decoration-color) +} +.app-nav>ul>li>a:hover,.app-nav>ul>li>span:hover{ + background:var(--navbar-root-background--hover, var(--navbar-root-background)); + border-style:var(--navbar-root-border-style--hover, var(--navbar-root-border-style)); + border-color:var(--navbar-root-border-color--hover, var(--navbar-root-border-color)); + color:var(--navbar-root-color--hover, var(--navbar-root-color)); + -webkit-text-decoration:var(--navbar-root-text-decoration--hover, var(--navbar-root-text-decoration)); + text-decoration:var(--navbar-root-text-decoration--hover, var(--navbar-root-text-decoration)); + -webkit-text-decoration-color:var(--navbar-root-text-decoration-color--hover, var(--navbar-root-text-decoration-color)); + text-decoration-color:var(--navbar-root-text-decoration-color--hover, var(--navbar-root-text-decoration-color)) +} +.app-nav>ul>li>a:not(:last-child),.app-nav>ul>li>span:not(:last-child){ + padding:var(--navbar-menu-root-padding, var(--navbar-root-padding)); + background:var(--navbar-menu-root-background, var(--navbar-root-background)) +} +.app-nav>ul>li>a:not(:last-child):hover,.app-nav>ul>li>span:not(:last-child):hover{ + background:var(--navbar-menu-root-background--hover, var(--navbar-menu-root-background, var(--navbar-root-background--hover, var(--navbar-root-background)))) +} +.app-nav>ul>li>a.active{ + background:var(--navbar-root-background--active, var(--navbar-root-background)); + border-style:var(--navbar-root-border-style--active, var(--navbar-root-border-style)); + border-color:var(--navbar-root-border-color--active, var(--navbar-root-border-color)); + color:var(--navbar-root-color--active, var(--navbar-root-color)); + -webkit-text-decoration:var(--navbar-root-text-decoration--active, var(--navbar-root-text-decoration)); + text-decoration:var(--navbar-root-text-decoration--active, var(--navbar-root-text-decoration)); + -webkit-text-decoration-color:var(--navbar-root-text-decoration-color--active, var(--navbar-root-text-decoration-color)); + text-decoration-color:var(--navbar-root-text-decoration-color--active, var(--navbar-root-text-decoration-color)) +} +.app-nav>ul>li>a.active:not(:last-child):hover{ + background:var(--navbar-menu-root-background--active, var(--navbar-menu-root-background, var(--navbar-root-background--active, var(--navbar-root-background)))) +} +.app-nav>ul>li ul{ + visibility:hidden; + position:absolute; + top:100%; + right:50%; + overflow-y:auto; + box-sizing:border-box; + max-height:50vh; + padding:var(--navbar-menu-padding); + border-width:var(--navbar-menu-border-width, 0); + border-style:solid; + border-color:var(--navbar-menu-border-color); + border-radius:var(--navbar-menu-border-radius); + background:var(--navbar-menu-background); + box-shadow:var(--navbar-menu-box-shadow); + text-align:left; + white-space:nowrap; + opacity:0; + transform:translate(50%, -0.35em); + transition:var(--navbar-menu-transition) +} +.app-nav>ul>li ul li{ + white-space:nowrap +} +.app-nav>ul>li ul a{ + margin:var(--navbar-menu-link-margin); + padding:var(--navbar-menu-link-padding); + border-width:var(--navbar-menu-link-border-width, 0); + border-style:var(--navbar-menu-link-border-style); + border-color:var(--navbar-menu-link-border-color); + border-radius:var(--navbar-menu-link-border-radius); + background:var(--navbar-menu-link-background); + color:var(--navbar-menu-link-color); + -webkit-text-decoration:var(--navbar-menu-link-text-decoration); + text-decoration:var(--navbar-menu-link-text-decoration); + -webkit-text-decoration-color:var(--navbar-menu-link-text-decoration-color); + text-decoration-color:var(--navbar-menu-link-text-decoration-color) +} +.app-nav>ul>li ul a:hover{ + background:var(--navbar-menu-link-background--hover, var(--navbar-menu-link-background)); + border-style:var(--navbar-menu-link-border-style--hover, var(--navbar-menu-link-border-style)); + border-color:var(--navbar-menu-link-border-color--hover, var(--navbar-menu-link-border-color)); + color:var(--navbar-menu-link-color--hover, var(--navbar-menu-link-color)); + -webkit-text-decoration:var(--navbar-menu-link-text-decoration--hover, var(--navbar-menu-link-text-decoration)); + text-decoration:var(--navbar-menu-link-text-decoration--hover, var(--navbar-menu-link-text-decoration)); + -webkit-text-decoration-color:var(--navbar-menu-link-text-decoration-color--hover, var(--navbar-menu-link-text-decoration-color)); + text-decoration-color:var(--navbar-menu-link-text-decoration-color--hover, var(--navbar-menu-link-text-decoration-color)) +} +.app-nav>ul>li ul a.active{ + background:var(--navbar-menu-link-background--active, var(--navbar-menu-link-background)); + border-style:var(--navbar-menu-link-border-style--active, var(--navbar-menu-link-border-style)); + border-color:var(--navbar-menu-link-border-color--active, var(--navbar-menu-link-border-color)); + color:var(--navbar-menu-link-color--active, var(--navbar-menu-link-color)); + -webkit-text-decoration:var(--navbar-menu-link-text-decoration--active, var(--navbar-menu-link-text-decoration)); + text-decoration:var(--navbar-menu-link-text-decoration--active, var(--navbar-menu-link-text-decoration)); + -webkit-text-decoration-color:var(--navbar-menu-link-text-decoration-color--active, var(--navbar-menu-link-text-decoration-color)); + text-decoration-color:var(--navbar-menu-link-text-decoration-color--active, var(--navbar-menu-link-text-decoration-color)) +} +.app-nav>ul>li:hover ul,.app-nav>ul>li:focus ul,.app-nav>ul>li.focus-within ul{ + visibility:visible; + opacity:1; + transform:translate(50%, 0) +} +@media(min-width: 48em){ + nav.app-nav{ + margin-left:var(--sidebar-width) + } +} +.sidebar,.sidebar-toggle,.sidebar+.content{ + transition:all var(--sidebar-transition-duration) ease-out +} +@media(min-width: 48em){ + .sidebar+.content{ + margin-left:var(--sidebar-width) + } +} +.sidebar{ + display:flex; + flex-direction:column; + position:fixed; + z-index:10; + top:0; + right:100%; + overflow-x:hidden; + overflow-y:auto; + height:100vh; + width:var(--sidebar-width); + padding:var(--sidebar-padding); + border-width:var(--sidebar-border-width); + border-style:solid; + border-color:var(--sidebar-border-color); + background:var(--sidebar-background) +} +.sidebar>h1{ + margin:0; + margin:var(--sidebar-name-margin); + padding:var(--sidebar-name-padding); + background:var(--sidebar-name-background); + color:var(--sidebar-name-color); + font-family:var(--sidebar-name-font-family); + font-size:var(--sidebar-name-font-size); + font-weight:var(--sidebar-name-font-weight); + text-align:var(--sidebar-name-text-align) +} +.sidebar>h1 img{ + max-width:100% +} +.sidebar>h1 .app-name-link{ + color:var(--sidebar-name-color) +} +body:not([data-platform^=Mac]) .sidebar::-webkit-scrollbar{ + width:5px +} +body:not([data-platform^=Mac]) .sidebar::-webkit-scrollbar-thumb{ + border-radius:50vw +} +@media(min-width: 48em){ + .sidebar{ + position:absolute; + transform:translateX(var(--sidebar-width)) + } +} +@media print{ + .sidebar{ + display:none + } +} +.sidebar-nav,.sidebar nav{ + order:1; + margin:var(--sidebar-nav-margin); + padding:var(--sidebar-nav-padding); + background:var(--sidebar-nav-background) +} +.sidebar-nav ul,.sidebar nav ul{ + margin:0; + padding:0; + list-style:none +} +.sidebar-nav ul ul,.sidebar nav ul ul{ + margin-left:var(--sidebar-nav-indent) +} +.sidebar-nav a,.sidebar nav a{ + display:block; + overflow:hidden; + margin:var(--sidebar-nav-link-margin); + padding:var(--sidebar-nav-link-padding); + border-width:var(--sidebar-nav-link-border-width, 0); + border-style:var(--sidebar-nav-link-border-style); + border-color:var(--sidebar-nav-link-border-color); + border-radius:var(--sidebar-nav-link-border-radius); + background:var(--sidebar-nav-link-background); + color:var(--sidebar-nav-link-color); + font-weight:var(--sidebar-nav-link-font-weight); + white-space:nowrap; + -webkit-text-decoration:var(--sidebar-nav-link-text-decoration); + text-decoration:var(--sidebar-nav-link-text-decoration); + -webkit-text-decoration-color:var(--sidebar-nav-link-text-decoration-color); + text-decoration-color:var(--sidebar-nav-link-text-decoration-color); + text-overflow:ellipsis; + transition:var(--sidebar-nav-link-transition) +} +.sidebar-nav a img,.sidebar nav a img{ + margin-top:-0.25em; + vertical-align:middle +} +.sidebar-nav a img:first-child,.sidebar nav a img:first-child{ + margin-right:.5em +} +.sidebar-nav a:hover,.sidebar nav a:hover{ + border-width:var(--sidebar-nav-link-border-width--hover, var(--sidebar-nav-link-border-width, 0)); + border-style:var(--sidebar-nav-link-border-style--hover, var(--sidebar-nav-link-border-style)); + border-color:var(--sidebar-nav-link-border-color--hover, var(--sidebar-nav-link-border-color)); + background:var(--sidebar-nav-link-background--hover, var(--sidebar-nav-link-background)); + color:var(--sidebar-nav-link-color--hover, var(--sidebar-nav-link-color)); + font-weight:var(--sidebar-nav-link-font-weight--hover, var(--sidebar-nav-link-font-weight)); + -webkit-text-decoration:var(--sidebar-nav-link-text-decoration--hover, var(--sidebar-nav-link-text-decoration)); + text-decoration:var(--sidebar-nav-link-text-decoration--hover, var(--sidebar-nav-link-text-decoration)); + -webkit-text-decoration-color:var(--sidebar-nav-link-text-decoration-color); + text-decoration-color:var(--sidebar-nav-link-text-decoration-color) +} +.sidebar-nav ul>li>span,.sidebar-nav ul>li>strong,.sidebar nav ul>li>span,.sidebar nav ul>li>strong{ + display:block; + margin:var(--sidebar-nav-strong-margin); + padding:var(--sidebar-nav-strong-padding); + border-width:var(--sidebar-nav-strong-border-width, 0); + border-style:solid; + border-color:var(--sidebar-nav-strong-border-color); + color:var(--sidebar-nav-strong-color); + font-size:var(--sidebar-nav-strong-font-size); + font-weight:var(--sidebar-nav-strong-font-weight); + text-transform:var(--sidebar-nav-strong-text-transform) +} +.sidebar-nav ul>li>span+ul,.sidebar-nav ul>li>strong+ul,.sidebar nav ul>li>span+ul,.sidebar nav ul>li>strong+ul{ + margin-left:0 +} +.sidebar-nav ul>li:first-child>span,.sidebar-nav ul>li:first-child>strong,.sidebar nav ul>li:first-child>span,.sidebar nav ul>li:first-child>strong{ + margin-top:0 +} +.sidebar-nav::-webkit-scrollbar,.sidebar nav::-webkit-scrollbar{ + width:0 +} +@supports(width: env(safe-area-inset)){ + @media only screen and (orientation: landscape){ + .sidebar-nav,.sidebar nav{ + margin-left:calc(env(safe-area-inset-left)/2) + } + } +} +.sidebar-nav li>a:before,.sidebar-nav li>strong:before{ + display:inline-block +} +.sidebar-nav li>a{ + background-repeat:var(--sidebar-nav-pagelink-background-repeat); + background-size:var(--sidebar-nav-pagelink-background-size) +} +.sidebar-nav li>a[href^="/"]:not([href*="?id="]),.sidebar-nav li>a[href^="#/"]:not([href*="?id="]){ + transition:var(--sidebar-nav-pagelink-transition) +} +.sidebar-nav li>a[href^="/"]:not([href*="?id="]),.sidebar-nav li>a[href^="/"]:not([href*="?id="])~ul a,.sidebar-nav li>a[href^="#/"]:not([href*="?id="]),.sidebar-nav li>a[href^="#/"]:not([href*="?id="])~ul a{ + padding:var(--sidebar-nav-pagelink-padding, var(--sidebar-nav-link-padding)) +} +.sidebar-nav li>a[href^="/"]:not([href*="?id="]):only-child,.sidebar-nav li>a[href^="#/"]:not([href*="?id="]):only-child{ + background:var(--sidebar-nav-pagelink-background) +} +.sidebar-nav li>a[href^="/"]:not([href*="?id="]):not(:only-child),.sidebar-nav li>a[href^="#/"]:not([href*="?id="]):not(:only-child){ + background:var(--sidebar-nav-pagelink-background--loaded, var(--sidebar-nav-pagelink-background)) +} +.sidebar-nav li.active>a,.sidebar-nav li.collapse>a{ + border-width:var(--sidebar-nav-link-border-width--active, var(--sidebar-nav-link-border-width)); + border-style:var(--sidebar-nav-link-border-style--active, var(--sidebar-nav-link-border-style)); + border-color:var(--sidebar-nav-link-border-color--active, var(--sidebar-nav-link-border-color)); + background:var(--sidebar-nav-link-background--active, var(--sidebar-nav-link-background)); + color:var(--sidebar-nav-link-color--active, var(--sidebar-nav-link-color)); + font-weight:var(--sidebar-nav-link-font-weight--active, var(--sidebar-nav-link-font-weight)); + -webkit-text-decoration:var(--sidebar-nav-link-text-decoration--active, var(--sidebar-nav-link-text-decoration)); + text-decoration:var(--sidebar-nav-link-text-decoration--active, var(--sidebar-nav-link-text-decoration)); + -webkit-text-decoration-color:var(--sidebar-nav-link-text-decoration-color); + text-decoration-color:var(--sidebar-nav-link-text-decoration-color) +} +.sidebar-nav li.active>a[href^="/"]:not([href*="?id="]):not(:only-child),.sidebar-nav li.active>a[href^="#/"]:not([href*="?id="]):not(:only-child){ + background:var(--sidebar-nav-pagelink-background--active, var(--sidebar-nav-pagelink-background--loaded, var(--sidebar-nav-pagelink-background))) +} +.sidebar-nav li.collapse>a[href^="/"]:not([href*="?id="]):not(:only-child),.sidebar-nav li.collapse>a[href^="#/"]:not([href*="?id="]):not(:only-child){ + background:var(--sidebar-nav-pagelink-background--collapse, var(--sidebar-nav-pagelink-background--loaded, var(--sidebar-nav-pagelink-background))) +} +.sidebar-nav li.collapse .app-sub-sidebar{ + display:none +} +.sidebar-nav>ul>li>a:before{ + content:var(--sidebar-nav-link-before-content-l1, var(--sidebar-nav-link-before-content)); + margin:var(--sidebar-nav-link-before-margin-l1, var(--sidebar-nav-link-before-margin)); + color:var(--sidebar-nav-link-before-color-l1, var(--sidebar-nav-link-before-color)) +} +.sidebar-nav>ul>li.active>a:before{ + content:var(--sidebar-nav-link-before-content-l1--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l1, var(--sidebar-nav-link-before-content)))); + color:var(--sidebar-nav-link-before-color-l1--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l1, var(--sidebar-nav-link-before-color)))) +} +.sidebar-nav>ul>li>ul>li>a:before{ + content:var(--sidebar-nav-link-before-content-l2, var(--sidebar-nav-link-before-content)); + margin:var(--sidebar-nav-link-before-margin-l2, var(--sidebar-nav-link-before-margin)); + color:var(--sidebar-nav-link-before-color-l2, var(--sidebar-nav-link-before-color)) +} +.sidebar-nav>ul>li>ul>li.active>a:before{ + content:var(--sidebar-nav-link-before-content-l2--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l2, var(--sidebar-nav-link-before-content)))); + color:var(--sidebar-nav-link-before-color-l2--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l2, var(--sidebar-nav-link-before-color)))) +} +.sidebar-nav>ul>li>ul>li>ul>li>a:before{ + content:var(--sidebar-nav-link-before-content-l3, var(--sidebar-nav-link-before-content)); + margin:var(--sidebar-nav-link-before-margin-l3, var(--sidebar-nav-link-before-margin)); + color:var(--sidebar-nav-link-before-color-l3, var(--sidebar-nav-link-before-color)) +} +.sidebar-nav>ul>li>ul>li>ul>li.active>a:before{ + content:var(--sidebar-nav-link-before-content-l3--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l3, var(--sidebar-nav-link-before-content)))); + color:var(--sidebar-nav-link-before-color-l3--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l3, var(--sidebar-nav-link-before-color)))) +} +.sidebar-nav>ul>li>ul>li>ul>li>ul>li>a:before{ + content:var(--sidebar-nav-link-before-content-l4, var(--sidebar-nav-link-before-content)); + margin:var(--sidebar-nav-link-before-margin-l4, var(--sidebar-nav-link-before-margin)); + color:var(--sidebar-nav-link-before-color-l4, var(--sidebar-nav-link-before-color)) +} +.sidebar-nav>ul>li>ul>li>ul>li>ul>li.active>a:before{ + content:var(--sidebar-nav-link-before-content-l4--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l4, var(--sidebar-nav-link-before-content)))); + color:var(--sidebar-nav-link-before-color-l4--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l4, var(--sidebar-nav-link-before-color)))) +} +.sidebar-nav>:last-child{ + margin-bottom:2rem +} +.sidebar-toggle,.sidebar-toggle-button{ + width:var(--sidebar-toggle-width); + outline:none +} +.sidebar-toggle{ + position:fixed; + z-index:11; + top:0; + bottom:0; + left:0; + max-width:40px; + margin:0; + padding:0; + border:0; + background:rgba(0,0,0,0); + -webkit-appearance:none; + -moz-appearance:none; + appearance:none; + cursor:pointer +} +.sidebar-toggle .sidebar-toggle-button{ + position:absolute; + top:var(--sidebar-toggle-offset-top); + left:var(--sidebar-toggle-offset-left); + height:var(--sidebar-toggle-height); + border-radius:var(--sidebar-toggle-border-radius); + border-width:var(--sidebar-toggle-border-width); + border-style:var(--sidebar-toggle-border-style); + border-color:var(--sidebar-toggle-border-color); + background:var(--sidebar-toggle-background, transparent); + color:var(--sidebar-toggle-icon-color) +} +.sidebar-toggle span{ + position:absolute; + top:calc(50% - var(--sidebar-toggle-icon-stroke-width)/2); + left:calc(50% - var(--sidebar-toggle-icon-width)/2); + height:var(--sidebar-toggle-icon-stroke-width); + width:var(--sidebar-toggle-icon-width); + background-color:currentColor +} +.sidebar-toggle span:nth-child(1){ + margin-top:calc(0px - var(--sidebar-toggle-icon-height)/2) +} +.sidebar-toggle span:nth-child(3){ + margin-top:calc(var(--sidebar-toggle-icon-height)/2) +} +@media(min-width: 48em){ + .sidebar-toggle{ + position:absolute; + overflow:visible; + top:var(--sidebar-toggle-offset-top); + bottom:auto; + left:0; + height:var(--sidebar-toggle-height); + transform:translateX(var(--sidebar-width)) + } + .sidebar-toggle .sidebar-toggle-button{ + top:0 + } +} +@media print{ + .sidebar-toggle{ + display:none + } +} +@media(max-width: 47.99em){ + body.close .sidebar,body.close .sidebar-toggle,body.close .sidebar+.content{ + transform:translateX(var(--sidebar-width)) + } +} +@media(min-width: 48em){ + body.close .sidebar+.content{ + transform:translateX(0) + } +} +@media(max-width: 47.99em){ + body.close nav.app-nav,body.close .github-corner{ + display:none + } +} +@media(min-width: 48em){ + body.close .sidebar,body.close .sidebar-toggle{ + transform:translateX(0) + } +} +@media(min-width: 48em){ + body.close nav.app-nav{ + margin-left:0 + } +} +@media(max-width: 47.99em){ + body.close .sidebar-toggle{ + width:100%; + max-width:none + } + body.close .sidebar-toggle span{ + margin-top:0 + } + body.close .sidebar-toggle span:nth-child(1){ + transform:rotate(45deg) + } + body.close .sidebar-toggle span:nth-child(2){ + display:none + } + body.close .sidebar-toggle span:nth-child(3){ + transform:rotate(-45deg) + } +} +@media(min-width: 48em){ + body.close .sidebar+.content{ + margin-left:0 + } +} +@media(min-width: 48em){ + body.sticky .sidebar,body.sticky .sidebar-toggle{ + position:fixed + } +} +body .docsify-copy-code-button,body .docsify-copy-code-button:after{ + border-radius:var(--border-radius-m, 0); + border-top-left-radius:0; + border-bottom-right-radius:0; + background:var(--copycode-background); + color:var(--copycode-color) +} +body .docsify-copy-code-button span{ + border-radius:var(--border-radius-s, 0) +} +body .docsify-pagination-container{ + border-top:var(--pagination-border-top); + color:var(--pagination-color) +} +body .pagination-item-label{ + font-size:var(--pagination-label-font-size) +} +body .pagination-item-label svg{ + color:var(--pagination-label-color); + height:var(--pagination-chevron-height); + stroke:var(--pagination-chevron-stroke); + stroke-linecap:var(--pagination-chevron-stroke-linecap); + stroke-linejoin:var(--pagination-chevron-stroke-linecap); + stroke-width:var(--pagination-chevron-stroke-width) +} +body .pagination-item-title{ + color:var(--pagination-title-color); + font-size:var(--pagination-title-font-size) +} +body .app-name.hide{ + display:block +} +body .sidebar{ + padding:var(--sidebar-padding) +} +.sidebar .search input{ + padding:0; + line-height:1; + font-size:inherit +} +.sidebar .search .clear-button svg{ + transform:scale(1) +} +.sidebar .search{ + order:var(--search-flex-order); + margin:var(--search-margin); + padding:var(--search-padding); + background:var(--search-background) +} +.sidebar .search a{ + color:inherit +} +.sidebar .search h2{ + margin:var(--search-result-heading-margin); + font-size:var(--search-result-heading-font-size); + font-weight:var(--search-result-heading-font-weight); + color:var(--search-result-heading-color) +} +.sidebar .search .input-wrap{ + align-items:stretch; + margin:var(--search-input-margin); + background-color:var(--search-input-background-color); + border-width:var(--search-input-border-width, 0); + border-style:solid; + border-color:var(--search-input-border-color); + border-radius:var(--search-input-border-radius) +} +.sidebar .search input[type=search]{ + min-width:0; + padding:var(--search-input-padding); + border:none; + background-color:rgba(0,0,0,0); + background-image:var(--search-input-background-image); + background-position:var(--search-input-background-position); + background-repeat:var(--search-input-background-repeat); + background-size:var(--search-input-background-size); + font-size:var(--search-input-font-size); + color:var(--search-input-color); + transition:var(--search-input-transition) +} +.sidebar .search input[type=search]::-ms-clear{ + display:none +} +.sidebar .search input[type=search]::-moz-placeholder{ + color:var(--search-input-placeholder-color, #808080) +} +.sidebar .search input[type=search]::placeholder{ + color:var(--search-input-placeholder-color, #808080) +} +.sidebar .search input[type=search]::-webkit-input-placeholder{ + line-height:normal +} +.sidebar .search input[type=search]:focus{ + background-color:var(--search-input-background-color--focus, var(--search-input-background-color)); + background-image:var(--search-input-background-image--focus, var(--search-input-background-image)); + background-position:var(--search-input-background-position--focus, var(--search-input-background-position)); + background-size:var(--search-input-background-size--focus, var(--search-input-background-size)) +} +@supports(width: env(safe-area-inset)){ + @media only screen and (orientation: landscape){ + .sidebar .search input[type=search]{ + margin-left:calc(env(safe-area-inset-left)/2) + } + } +} +.sidebar .search p{ + font-size:inherit; + overflow:hidden; + text-overflow:ellipsis; + -webkit-box-orient:vertical; + -webkit-line-clamp:2; + line-clamp:2 +} +.sidebar .search p:empty{ + text-align:center +} +.sidebar .search .clear-button{ + width:auto; + margin:0; + padding:0 10px; + border:none; + line-height:1; + background:rgba(0,0,0,0); + cursor:pointer +} +.sidebar .search .clear-button svg circle{ + fill:var(--search-clear-icon-color1, #808080) +} +.sidebar .search .clear-button svg path{ + stroke:var(--search-clear-icon-color2, #fff) +} +.sidebar .search.show~*:not(h1){ + display:none +} +.sidebar .search .results-panel{ + display:none; + color:var(--search-result-item-color); + font-size:var(--search-result-item-font-size); + font-weight:var(--search-result-item-font-weight) +} +.sidebar .search .results-panel.show{ + display:block +} +.sidebar .search .matching-post{ + border:none; + margin:var(--search-result-item-margin); + padding:var(--search-result-item-padding) +} +.sidebar .search .matching-post,.sidebar .search .matching-post:last-child{ + border-width:var(--search-result-item-border-width, 0) !important; + border-style:var(--search-result-item-border-style); + border-color:var(--search-result-item-border-color) +} +.sidebar .search .matching-post p{ + margin:0 +} +.sidebar .search .search-keyword{ + margin:var(--search-result-keyword-margin); + padding:var(--search-result-keyword-padding); + border-radius:var(--search-result-keyword-border-radius); + background-color:var(--search-result-keyword-background); + color:var(--search-result-keyword-color, currentColor); + font-style:normal; + font-weight:var(--search-result-keyword-font-weight) +} +.medium-zoom-overlay,.medium-zoom-image--open,.medium-zoom-image--opened{ + z-index:2147483646 !important +} +.medium-zoom-overlay{ + background:var(--zoomimage-overlay-background) !important +} diff --git a/lib/averages/Afirma.cs b/lib/averages/Afirma.cs index e2771a61..3832ad55 100644 --- a/lib/averages/Afirma.cs +++ b/lib/averages/Afirma.cs @@ -2,88 +2,150 @@ namespace QuanTAlib; public class Afirma : AbstractBase { - private readonly int Period; - private readonly CircularBuffer _buffer; - private readonly double _alpha; // Adaptive factor - private double _lastAfirma, _p_lastAfirma; - private double _lastError, _p_lastError; - public Afirma(int period, double alpha = 0.1) + public enum WindowType { - if (period < 1) + 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(period), "Period must be greater than or equal to 1."); + throw new ArgumentOutOfRangeException(nameof(periods), "Periods must be greater than or equal to 1."); } - if (alpha <= 0 || alpha >= 1) + if (taps < 1) { - throw new ArgumentOutOfRangeException(nameof(alpha), "Alpha must be between 0 and 1 (exclusive)."); + throw new ArgumentOutOfRangeException(nameof(taps), "Taps must be greater than or equal to 1."); } - Period = period; - WarmupPeriod = period; - _buffer = new CircularBuffer(period); - _alpha = alpha; + 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"; - WarmupPeriod = period; Init(); } - public Afirma(object source, int period, double alpha = 0.1) : this(period: period, alpha: alpha) + 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)); } - public override void Init() - { - base.Init(); - _lastAfirma = 0; - _lastError = 0; - } - protected override void ManageState(bool isNew) { if (isNew) { _lastValidValue = Input.Value; _index++; - _p_lastAfirma = _lastAfirma; - _p_lastError = _lastError; - } - else - { - _lastAfirma = _p_lastAfirma; - _lastError = _p_lastError; } } - /// - /// Core AFIRMA calculation - /// protected override double Calculation() { - double result; ManageState(IsNew); _buffer.Add(Input.Value, Input.IsNew); - if (_index < Period) + if (_index >= Taps) { - // Use simple average during warmup period - result = _buffer.Average(); - } - else - { - // AFIRMA calculation - double sma = _buffer.Average(); - double error = Input.Value - _lastAfirma; - double denominator = Math.Abs(error) + Math.Abs(_lastError); - double adaptiveFactor = denominator != 0 ? _alpha * Math.Abs(error) / denominator : _alpha; - result = sma + adaptiveFactor * (Input.Value - sma); + double a0 = _buffer[_n]; + double a1 = _buffer[_n] - _buffer[_n + 1]; + double sx2y = 0.0; + double sx3y = 0.0; - _lastError = error; + 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; } - _lastAfirma = result; IsHot = _index >= WarmupPeriod; return result; } -} \ No newline at end of file + + 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; + } + +} diff --git a/lib/averages/Alma.cs b/lib/averages/Alma.cs index dd031ffd..6f363795 100644 --- a/lib/averages/Alma.cs +++ b/lib/averages/Alma.cs @@ -25,7 +25,7 @@ public class Alma : AbstractBase /// Controls the smoothness and high-frequency filtering. Default is 0.85. /// Controls the shape of the Gaussian distribution. Default is 6. /// Thrown when period is less than 1. - 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) { diff --git a/lib/averages/Convolution.cs b/lib/averages/Convolution.cs index 470f426c..60226d61 100644 --- a/lib/averages/Convolution.cs +++ b/lib/averages/Convolution.cs @@ -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) { diff --git a/lib/averages/Dema.cs b/lib/averages/Dema.cs index afd57c81..d3e37bfb 100644 --- a/lib/averages/Dema.cs +++ b/lib/averages/Dema.cs @@ -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) { diff --git a/lib/averages/Dsma.cs b/lib/averages/Dsma.cs index 98c7b7e3..5fb0c9ba 100644 --- a/lib/averages/Dsma.cs +++ b/lib/averages/Dsma.cs @@ -52,13 +52,13 @@ public class Dsma : AbstractBase // SuperSmoother filter coefficients double _a1 = Math.Exp(-1.414 * Math.PI / (0.5 * period)); double _b1 = 2 * _a1 * Math.Cos(1.414 * Math.PI / (0.5 * period)); - + _c2 = _b1; _c3 = -_a1 * _a1; _c1 = 1 - _c2 - _c3; Name = "Dsma"; - WarmupPeriod = (int) (period * 1.5); // A conservative estimate + WarmupPeriod = (int)(period * 1.5); // A conservative estimate Init(); } diff --git a/lib/averages/Dwma.cs b/lib/averages/Dwma.cs index ec7fc883..ba09d84c 100644 --- a/lib/averages/Dwma.cs +++ b/lib/averages/Dwma.cs @@ -6,11 +6,6 @@ namespace QuanTAlib; /// The weights are decreasing over the period with p^2 decay, and the most recent data has the heaviest weight. ///
/// -/// Smoothness: ★★★★★ (5/5) -/// Sensitivity: ★★★☆☆ (3/5) -/// Overshooting: ★★★★☆ (4/5) -/// Lag: ★★☆☆☆ (2/5) -/// /// The DWMA is calculated by applying two WMAs in sequence: /// 1. An inner WMA is applied to the input data. /// 2. An outer WMA is then applied to the result of the inner WMA. @@ -28,7 +23,6 @@ namespace QuanTAlib; public class Dwma : AbstractBase { - private readonly int _period; private readonly Wma _innerWma; private readonly Wma _outerWma; @@ -38,11 +32,10 @@ public class Dwma : AbstractBase { throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); } - _period = period; _innerWma = new Wma(period); _outerWma = new Wma(period); Name = "Wma"; - WarmupPeriod = 2 * _period - 1; + WarmupPeriod = 2 * period - 1; Init(); } diff --git a/lib/averages/Ema.cs b/lib/averages/Ema.cs index 056ec24a..3e38f745 100644 --- a/lib/averages/Ema.cs +++ b/lib/averages/Ema.cs @@ -2,10 +2,11 @@ namespace QuanTAlib; /// /// EMA: Exponential Moving Average -/// EMA needs very short history buffer and calculates the EMA value using just the -/// previous EMA value. The weight of the new datapoint (alpha) is alpha = 2 / (period + 1) /// /// +/// EMA needs very short history buffer and calculates the EMA value using just the +/// previous EMA value. The weight of the new datapoint (alpha) is alpha = 2 / (period + 1) +/// /// Key characteristics: /// - Uses no buffer, relying only on the previous EMA value. /// - The weight of new data points is calculated as alpha = 2 / (period + 1). @@ -19,18 +20,53 @@ namespace QuanTAlib; /// - https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp /// - https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA /// - public class Ema : AbstractBase { // inherited _index // inherited _value - private readonly int _period; - private CircularBuffer _sma; - private double _lastEma, _p_lastEma; - private double _k, _e, _p_e; - private bool _isInit, _p_isInit, _useSma; - public Ema(int period, bool useSma = true) : base() + /// + /// The period for the EMA calculation. + /// + private readonly int _period; + + /// + /// Circular buffer for SMA calculation. + /// + private CircularBuffer _sma; + + /// + /// The last calculated EMA value. + /// + private double _lastEma, _p_lastEma; + + /// + /// Compensator for early EMA values. + /// + private double _e, _p_e; + + /// + /// The smoothing factor for EMA calculation. + /// + private readonly double _k; + + /// + /// Flags to track initialization status. + /// + private bool _isInit, _p_isInit; + + /// + /// Flag to determine whether to use SMA for initial values. + /// + private readonly bool _useSma; + + /// + /// Initializes a new instance of the Ema class with a specified period. + /// + /// The period for EMA calculation. + /// Whether to use SMA for initial values. Default is true. + /// Thrown when period is less than 1. + public Ema(int period, bool useSma = true) { if (period < 1) { @@ -45,23 +81,36 @@ public class Ema : AbstractBase Init(); } - public Ema(double alpha) : base() + /// + /// Initializes a new instance of the Ema class with a specified alpha value. + /// + /// The smoothing factor for EMA calculation. + public Ema(double alpha) { _k = alpha; _useSma = false; _sma = new(1); + Name = "Ema"; _period = 1; WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - _k)); //95th percentile Init(); } + /// + /// Initializes a new instance of the Ema class with a specified source and period. + /// + /// The source object for event subscription. + /// The period for EMA calculation. + /// Whether to use SMA for initial values. Default is true. public Ema(object source, int period, bool useSma = true) : this(period, useSma) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } - //inhereted public void Sub(object source, in ValueEventArgs args) + /// + /// Initializes the Ema instance. + /// public override void Init() { base.Init(); @@ -72,6 +121,10 @@ public class Ema : AbstractBase _sma = new(_period); } + /// + /// Manages the state of the Ema instance. + /// + /// Indicates whether the input is new. protected override void ManageState(bool isNew) { if (isNew) @@ -90,8 +143,9 @@ public class Ema : AbstractBase } /// - /// Core EMA calculation + /// Performs the EMA calculation. /// + /// The calculated EMA value. protected override double Calculation() { double result, _ema; @@ -116,7 +170,7 @@ public class Ema : AbstractBase _ema = _k * (Input.Value - _lastEma) + _lastEma; // _useSma decides if we use compensator or not - result = (_useSma || _e == 0) ? _ema : _ema / (1 - _e); + result = (_useSma || _e <= double.Epsilon) ? _ema : _ema / (1 - _e); } _lastEma = _ema; IsHot = _index >= WarmupPeriod; diff --git a/lib/averages/Frama.cs b/lib/averages/Frama.cs index 8a260cf3..223b1458 100644 --- a/lib/averages/Frama.cs +++ b/lib/averages/Frama.cs @@ -1,99 +1,102 @@ 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); } } - 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); - 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; } } \ No newline at end of file diff --git a/lib/averages/Fwma.cs b/lib/averages/Fwma.cs index 9746036a..f5644d6a 100644 --- a/lib/averages/Fwma.cs +++ b/lib/averages/Fwma.cs @@ -2,7 +2,6 @@ namespace QuanTAlib; public class Fwma : AbstractBase { - private readonly int _period; private readonly Convolution _convolution; public Fwma(int period) @@ -11,8 +10,7 @@ public class Fwma : AbstractBase { throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); } - _period = period; - _convolution = new Convolution(GenerateKernel(_period)); + _convolution = new Convolution(GenerateKernel(period)); Name = "Fwma"; WarmupPeriod = period; Init(); diff --git a/lib/averages/Gma.cs b/lib/averages/Gma.cs index 4a0780fc..4f003f5c 100644 --- a/lib/averages/Gma.cs +++ b/lib/averages/Gma.cs @@ -2,7 +2,6 @@ namespace QuanTAlib; public class Gma : AbstractBase { - private readonly int _period; private readonly Convolution _convolution; public Gma(int period) @@ -11,8 +10,7 @@ public class Gma : AbstractBase { throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); } - _period = period; - _convolution = new Convolution(GenerateKernel(_period)); + _convolution = new Convolution(GenerateKernel(period)); Name = "Gma"; WarmupPeriod = period; Init(); diff --git a/lib/averages/Hma.cs b/lib/averages/Hma.cs index 6486d689..220b419d 100644 --- a/lib/averages/Hma.cs +++ b/lib/averages/Hma.cs @@ -2,7 +2,6 @@ namespace QuanTAlib; public class Hma : AbstractBase { - private readonly int _period, _sqrtPeriod; private readonly Convolution _wmaHalf, _wmaFull, _wmaFinal; public Hma(int period) @@ -11,13 +10,12 @@ public class Hma : AbstractBase { throw new ArgumentException("Period must be greater than or equal to 2.", nameof(period)); } - _period = period; - _sqrtPeriod = (int)Math.Sqrt(period); + int _sqrtPeriod = (int)Math.Sqrt(period); _wmaHalf = new Convolution(GenerateWmaKernel(period / 2)); _wmaFull = new Convolution(GenerateWmaKernel(period)); _wmaFinal = new Convolution(GenerateWmaKernel(_sqrtPeriod)); Name = "Hma"; - WarmupPeriod = _period + _sqrtPeriod - 1; + WarmupPeriod = period + _sqrtPeriod - 1; Init(); } diff --git a/lib/averages/Htit.cs b/lib/averages/Htit.cs index 08767763..487a89f5 100644 --- a/lib/averages/Htit.cs +++ b/lib/averages/Htit.cs @@ -1,7 +1,7 @@ //not working yet -//TODO consistency test +//TODO fails 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; } } \ No newline at end of file diff --git a/lib/averages/Hwma.cs b/lib/averages/Hwma.cs index b6e8d492..c2473349 100644 --- a/lib/averages/Hwma.cs +++ b/lib/averages/Hwma.cs @@ -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) { diff --git a/lib/averages/Jma.cs b/lib/averages/Jma.cs index e5a58b11..0d3777cb 100644 --- a/lib/averages/Jma.cs +++ b/lib/averages/Jma.cs @@ -1,62 +1,93 @@ -using QuanTAlib; -//TODO consistency test +/// +/// Represents a Jurik Moving Average, based on known and reverse-engineered insights +/// + +namespace QuanTAlib; + public class Jma : AbstractBase { - public readonly int Period; + private readonly double _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 _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; + private double _len1; + private double _pow1; + private readonly double _beta; + private double _upperBand, _lowerBand, _p_upperBand, _p_lowerBand; + private double _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma; + private double _vSum, _p_vSum; - public Jma(int period, double phase = 0, int vshort = 10) : base() + + public double UpperBand { get; set; } + public double LowerBand { get; set; } + public double Volty { get; set; } + public double Factor { get; set; } + + /// + /// Initializes a new instance of the Jma class with the specified parameters. + /// + /// The period over which to calculate the Jvolty. + /// The phase parameter for the JMA-style calculation. + /// + /// Thrown when period is less than 1. + /// + public Jma(int period, int phase = 0, double factor = 0.45, int buffer = 10) { if (period < 1) { throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); } - Period = period; - _vshort = vshort; - _vlong = 65; + Factor = factor; + _period = period; _phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5); - _values = new CircularBuffer(period); - _voltyShort = new CircularBuffer(vshort); - _vsumBuff = new CircularBuffer(_vlong); - _avoltyBuff = new CircularBuffer(2); + _vsumBuff = new CircularBuffer(buffer); + _avoltyBuff = new CircularBuffer(65); + _beta = factor * (_period - 1) / (factor * (_period - 1) + 2); - Name = "JMA"; WarmupPeriod = period * 2; - Init(); + Name = $"JMA({period})"; } + /// + /// Initializes a new instance of the Jvolty class with the specified source and parameters. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Jvolty. + /// The phase parameter for the JMA-style calculation. + public Jma(object source, int period, int phase = 0, double factor = 0.45, int buffer = 10) : this(period, phase, factor, buffer) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Jma instance by setting up the initial state. + /// public override void Init() { - _upperBand = _lowerBand = _prevMa1 = _prevDet0 = _prevDet1 = _prevJma = 0.0; - _p_UpperBand = _p_LowerBand = _p_prevMa1 = _p_prevDet0 = _p_prevDet1 = _p_prevJma = 0.0; - _beta = 0.45 * (Period - 1) / (0.45 * (Period - 1) + 2); - _len1 = Math.Max((Math.Log(Math.Sqrt(Period - 1)) / Math.Log(2.0)) + 2.0, 0); + base.Init(); + _upperBand = _lowerBand = 0.0; + _p_upperBand = _p_lowerBand = 0.0; + _len1 = Math.Max((Math.Log(Math.Sqrt(_period - 1)) / Math.Log(2.0)) + 2.0, 0); _pow1 = Math.Max(_len1 - 2.0, 0.5); _avoltyBuff.Clear(); - _avoltyBuff.Add(0, true); - _avoltyBuff.Add(0, true); - base.Init(); + _vsumBuff.Clear(); } + /// + /// Manages the state of the Jma instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. protected override void ManageState(bool isNew) { if (isNew) { - _lastValidValue = Input.Value; _index++; - // Save current state - _p_UpperBand = _upperBand; - _p_LowerBand = _lowerBand; + _p_upperBand = _upperBand; + _p_lowerBand = _lowerBand; + _p_vSum = _vSum; _p_prevMa1 = _prevMa1; _p_prevDet0 = _prevDet0; _p_prevDet1 = _prevDet1; @@ -64,67 +95,69 @@ public class Jma : AbstractBase } else { - // Restore previous state - _upperBand = _p_UpperBand; - _lowerBand = _p_LowerBand; + _upperBand = _p_upperBand; + _lowerBand = _p_lowerBand; + _vSum = _p_vSum; _prevMa1 = _p_prevMa1; _prevDet0 = _p_prevDet0; _prevDet1 = _p_prevDet1; _prevJma = _p_prevJma; - } } + + /// + /// Performs the Jma calculation for the current value. + /// + /// + /// The calculated Jma value for the current input. + /// protected override double Calculation() { ManageState(Input.IsNew); - _values.Add(Input.Value, Input.IsNew); - - if (_index == 1) + double price = Input.Value; + if (_index <= 1) { - _prevMa1 = _prevJma = Input.Value; - return Input.Value; + _upperBand = _lowerBand = price; + _prevMa1 = _prevJma = price; } - double hprice = _values.Max(); - double lprice = _values.Min(); - - double del1 = hprice - _upperBand; - double del2 = lprice - _lowerBand; + double del1 = price - _upperBand; + double del2 = price - _lowerBand; double volty = Math.Max(Math.Abs(del1), Math.Abs(del2)); - _voltyShort.Add(volty, Input.IsNew); - double vsum = _vsumBuff.Newest() + 0.1 * (volty - _voltyShort.Oldest()); - _vsumBuff.Add(vsum, Input.IsNew); + _vsumBuff.Add(volty, Input.IsNew); + _vSum += (_vsumBuff[^1] - _vsumBuff[0]) / _vsumBuff.Count; + _avoltyBuff.Add(_vSum, Input.IsNew); + double avgvolty = _avoltyBuff.Average(); - double prevAvolty = _avoltyBuff.Newest(); - double avolty = prevAvolty + 2.0 / (Math.Max(4.0 * Period, 30) + 1.0) * (vsum - prevAvolty); - _avoltyBuff.Add(avolty, Input.IsNew); + double rvolty = (avgvolty > 0) ? volty / avgvolty : 1; + rvolty = Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1)); - double dVolty = (avolty > 0) ? volty / avolty : 0; - dVolty = Math.Min(Math.Max(dVolty, 1.0), Math.Pow(_len1, 1.0 / _pow1)); + double pow2 = Math.Pow(rvolty, _pow1); + double Kv = Math.Pow(_beta, Math.Sqrt(pow2)); - double pow2 = Math.Pow(dVolty, _pow1); - double len2 = Math.Sqrt(0.5 * (Period - 1)) * _len1; - double _Kv = Math.Pow(len2 / (len2 + 1), Math.Sqrt(pow2)); + _upperBand = (del1 >= 0) ? price : price - (Kv * del1); + _lowerBand = (del2 <= 0) ? price : price - (Kv * del2); - _upperBand = (del1 > 0) ? hprice : hprice - (_Kv * del1); - _lowerBand = (del2 < 0) ? lprice : lprice - (_Kv * del2); - - double alpha = Math.Pow(_beta, pow2); - double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1; + double _alpha = Math.Pow(_beta, pow2); + double ma1 = Input.Value + _alpha * (_prevMa1 - Input.Value); //original: (1 - _alpha) * Input.Value + _alpha * _prevMa1; _prevMa1 = ma1; - double det0 = (1 - _beta) * (Input.Value - ma1) + _beta * _prevDet0; + double det0 = price + _beta * (_prevDet0 - price + ma1) - ma1; //original: (price - ma1) * (1 - _beta) + _beta * _prevDet0; _prevDet0 = det0; - double ma2 = ma1 + (_phase + 1) * det0; + double ma2 = ma1 + _phase * det0; - double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - _prevJma)) + (alpha * alpha * _prevDet1); + double det1 = ((ma2 - _prevJma) * (1 - _alpha) * (1 - _alpha)) + (_alpha * _alpha * _prevDet1); _prevDet1 = det1; double jma = _prevJma + det1; _prevJma = jma; + UpperBand = _upperBand; + LowerBand = _lowerBand; + Volty = volty; + IsHot = _index >= WarmupPeriod; return jma; } -} \ No newline at end of file +} diff --git a/lib/averages/Kama.cs b/lib/averages/Kama.cs index 81cda9cf..e509c5e3 100644 --- a/lib/averages/Kama.cs +++ b/lib/averages/Kama.cs @@ -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) { diff --git a/lib/averages/Ltma.cs b/lib/averages/Ltma.cs index 5d64d249..e0aca68b 100644 --- a/lib/averages/Ltma.cs +++ b/lib/averages/Ltma.cs @@ -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."); diff --git a/lib/averages/Maaf.cs b/lib/averages/Maaf.cs index 8230d4e8..2b31ddf0 100644 --- a/lib/averages/Maaf.cs +++ b/lib/averages/Maaf.cs @@ -1,30 +1,31 @@ -//TODO: consistency test - namespace QuanTAlib; // https://efs.kb.esignal.com/hc/en-us/articles/6362791434395-2005-Mar-The-Secret-Behind-The-Filter-MedianAdaptiveFilter-efs +//TODO Fix initial values + 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; + _period = period; + _threshold = threshold; _priceBuffer = new CircularBuffer(4); - _smoothBuffer = new CircularBuffer(Period); + _smoothBuffer = new CircularBuffer(period); Name = "MAAF"; - WarmupPeriod = Period; + WarmupPeriod = period; Init(); } - public Maaf(object source, int Period = 39, double Threshold = 0.002) : this(Period, Threshold) + public Maaf(object source, int period = 39, double threshold = 0.002) : this(period, threshold) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); @@ -69,6 +70,7 @@ public class Maaf : AbstractBase double smooth = (_priceBuffer[^1] + (2 * _priceBuffer[^2]) + (2 * _priceBuffer[^3]) + _priceBuffer[^4]) / 6; _smoothBuffer.Add(smooth, Input.IsNew); + if (_smoothBuffer.Count < _period) { return smooth; diff --git a/lib/averages/Mama.cs b/lib/averages/Mama.cs index de303cb4..3af81ddb 100644 --- a/lib/averages/Mama.cs +++ b/lib/averages/Mama.cs @@ -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})"; @@ -40,7 +39,6 @@ public class Mama : AbstractBase public override void Init() { Fama = new TValue(); - base.Init(); } protected override void ManageState(bool isNew) diff --git a/lib/averages/Mgdi.cs b/lib/averages/Mgdi.cs index 59970815..27d8dd4a 100644 --- a/lib/averages/Mgdi.cs +++ b/lib/averages/Mgdi.cs @@ -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) { @@ -22,7 +22,7 @@ public class Mgdi : AbstractBase Init(); } - public Mgdi(object source, int period, double kFactor = 1.0) : this(period, kFactor) + public Mgdi(object source, int period, double kFactor = 0.6) : this(period, kFactor) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); @@ -40,7 +40,9 @@ public class Mgdi : AbstractBase { _p_prevMd = _prevMd; _index++; - } else { + } + else + { _prevMd = _p_prevMd; } } @@ -50,13 +52,15 @@ public class Mgdi : AbstractBase ManageState(Input.IsNew); double value = Input.Value; - if (_index < 2){ + if (_index < 2) + { _prevMd = value; } else { + double ratio = _prevMd != 0 ? value / _prevMd : 1; double md = _prevMd + ((value - _prevMd) / - (_kFactor * _period * Math.Pow(value / _prevMd, 4))); + (_kFactor * _period * Math.Pow(ratio, 4))); _prevMd = md; } diff --git a/lib/averages/Mma.cs b/lib/averages/Mma.cs index 0a7d27a6..cc02e1bc 100644 --- a/lib/averages/Mma.cs +++ b/lib/averages/Mma.cs @@ -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++; } } -} \ No newline at end of file + + 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; + } +} diff --git a/lib/averages/Pwma.cs b/lib/averages/Pwma.cs new file mode 100644 index 00000000..fc2aac76 --- /dev/null +++ b/lib/averages/Pwma.cs @@ -0,0 +1,85 @@ +namespace QuanTAlib; + +public class Pwma : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Pwma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Pwma"; + WarmupPeriod = period; + Init(); + } + + public Pwma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + + // Adjust for partial periods during warmup + if (_index < _period) + { + double[] partialKernel = GenerateKernel(_index); + result /= partialKernel.Sum(); + } + + IsHot = _index >= WarmupPeriod; + + return result; + } + + public static double[] GenerateKernel(int period) + { + double[] kernel = new double[period]; + kernel[0] = 1; + + for (int i = 1; i < period; i++) + { + for (int j = i; j > 0; j--) + { + kernel[j] += kernel[j - 1]; + } + } + + // Normalize the kernel + double weightSum = kernel.Sum(); + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } +} diff --git a/lib/averages/Qema.cs b/lib/averages/Qema.cs index f032cd07..d09e6ef0 100644 --- a/lib/averages/Qema.cs +++ b/lib/averages/Qema.cs @@ -2,31 +2,26 @@ namespace QuanTAlib; public class Qema : AbstractBase { - private readonly double _k1, _k2, _k3, _k4; + private readonly Ema _ema1, _ema2, _ema3, _ema4; private double _lastQema, _p_lastQema; - 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) { - if (k1 <= 0 || k2 <= 0 || k3 <= 0 || k4 <= 0 ) + if (k1 <= 0 || k2 <= 0 || k3 <= 0 || k4 <= 0) { - throw new ArgumentOutOfRangeException("All k values must be in the range (0, 1]."); + throw new ArgumentOutOfRangeException(nameof(k1), "All k values must be in the range (0, 1]."); } - _k1 = k1; - _k2 = k2; - _k3 = k3; - _k4 = k4; - _ema1 = new Ema(k1); _ema2 = new Ema(k2); _ema3 = new Ema(k3); _ema4 = new Ema(k4); Name = $"QEMA ({k1:F2},{k2:F2},{k3:F2},{k4:F2})"; - double smK = Math.Min(Math.Min(_k1, _k2), Math.Min(_k3, _k4)); + double smK = Math.Min(Math.Min(k1, k2), Math.Min(k3, k4)); - WarmupPeriod = (int) ((2 - smK) / smK); + WarmupPeriod = (int)((2 - smK) / smK); Init(); } diff --git a/lib/averages/Rema.cs b/lib/averages/Rema.cs index c89782c7..afa22046 100644 --- a/lib/averages/Rema.cs +++ b/lib/averages/Rema.cs @@ -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(); diff --git a/lib/averages/Rma.cs b/lib/averages/Rma.cs index 3dadbba2..628d8310 100644 --- a/lib/averages/Rma.cs +++ b/lib/averages/Rma.cs @@ -1,65 +1,162 @@ -using System; -using System.Runtime.CompilerServices; +namespace QuanTAlib; -namespace QuanTAlib { +/// +/// RMA: Relative Moving Average (also known as Wilder's Moving Average) +/// +/// +/// RMA is similar to EMA but uses a different smoothing factor. +/// +/// Key characteristics: +/// - Uses no buffer, relying only on the previous RMA value. +/// - The weight of new data points (alpha) is calculated as 1 / period. +/// - Provides a smoother curve compared to SMA and EMA, reacting more slowly to price changes. +/// +/// Calculation method: +/// This implementation can use SMA for the first Period bars as a seeding value for RMA when useSma is true. +/// +/// Sources: +/// - https://www.tradingview.com/pine-script-reference/v5/#fun_ta{dot}rma +/// - https://www.investopedia.com/terms/w/wilders-smoothing.asp +/// +public class Rma : AbstractBase +{ + // inherited _index + // inherited _value -public class Rma : AbstractBase { + /// + /// The period for the RMA calculation. + /// private readonly int _period; - private double _alpha; - private double _lastRMA; - private double _savedLastRMA; - public Rma(int period) : base() { - if (period < 1) { - throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + /// + /// Circular buffer for SMA calculation. + /// + private CircularBuffer _sma; + + /// + /// The last calculated RMA value. + /// + private double _lastRma, _p_lastRma; + + /// + /// Compensator for early RMA values. + /// + private double _e, _p_e; + + /// + /// The smoothing factor for RMA calculation. + /// + private readonly double _k; + + /// + /// Flags to track initialization status. + /// + private bool _isInit, _p_isInit; + + /// + /// Flag to determine whether to use SMA for initial values. + /// + private readonly bool _useSma; + + /// + /// Initializes a new instance of the Rma class with a specified period. + /// + /// The period for RMA calculation. + /// Whether to use SMA for initial values. Default is true. + /// Thrown when period is less than 1. + public Rma(int period, bool useSma = true) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); } _period = period; - WarmupPeriod = period * 2; - _alpha = 1.0 / _period; // Wilder's smoothing factor - Name = $"Rma({_period})"; + _k = 1.0 / _period; // Wilder's smoothing factor + _useSma = useSma; + _sma = new(period); + Name = "Rma"; + WarmupPeriod = _period * 2; // RMA typically needs more warmup periods Init(); } - public Rma(object source, int period) : this(period) { + /// + /// Initializes a new instance of the Rma class with a specified source and period. + /// + /// The source object for event subscription. + /// The period for RMA calculation. + /// Whether to use SMA for initial values. Default is true. + public Rma(object source, int period, bool useSma = true) : this(period, useSma) + { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } - public override void Init() { + /// + /// Initializes the Rma instance. + /// + public override void Init() + { base.Init(); - _lastRMA = 0; - _savedLastRMA = 0; + _e = 1.0; + _lastRma = 0; + _isInit = false; + _p_isInit = false; + _sma = new(_period); } - protected override void ManageState(bool isNew) { - if (isNew) { - _savedLastRMA = _lastRMA; - _lastValidValue = Input.Value; + /// + /// Manages the state of the Rma instance. + /// + /// Indicates whether the input is new. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_lastRma = _lastRma; + _p_isInit = _isInit; + _p_e = _e; _index++; - } else { - _lastRMA = _savedLastRMA; + } + else + { + _lastRma = _p_lastRma; + _isInit = _p_isInit; + _e = _p_e; } } - protected override double Calculation() { + /// + /// Performs the RMA calculation. + /// + /// The calculated RMA value. + protected override double Calculation() + { + double result, _rma; ManageState(Input.IsNew); - double rma; - - if (_index == 1) { - rma = Input.Value; - } else if (_index <= _period) { - // Simple average during initial period - rma = (_lastRMA * (_index - 1) + Input.Value) / _index; - } else { - // Wilder's smoothing method - rma = _alpha * (Input.Value - _lastRMA) + _lastRMA; + // when _UseSma == true, use SMA calculation until we have enough data points + if (!_isInit && _useSma) + { + _sma.Add(Input.Value, Input.IsNew); + _rma = _sma.Average(); + result = _rma; + if (_index >= _period) + { + _isInit = true; + } } + else + { + // compensator for early rma values + _e = (_e > 1e-10) ? (1 - _k) * _e : 0; - _lastRMA = rma; + _rma = _k * Input.Value + (1 - _k) * _lastRma; + + // _useSma decides if we use compensator or not + result = (_useSma || _e <= double.Epsilon) ? _rma : _rma / (1 - _e); + } + _lastRma = _rma; IsHot = _index >= WarmupPeriod; - - return rma; + return result; } } -} \ No newline at end of file diff --git a/lib/averages/Sinema.cs b/lib/averages/Sinema.cs index ddba6f94..6834dc64 100644 --- a/lib/averages/Sinema.cs +++ b/lib/averages/Sinema.cs @@ -2,7 +2,6 @@ namespace QuanTAlib; public class Sinema : AbstractBase { - private readonly int _period; private readonly Convolution _convolution; public Sinema(int period) @@ -11,8 +10,7 @@ public class Sinema : AbstractBase { throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); } - _period = period; - _convolution = new Convolution(GenerateKernel(_period)); + _convolution = new Convolution(GenerateKernel(period)); Name = "Sinema"; WarmupPeriod = period; Init(); diff --git a/lib/averages/Sma.cs b/lib/averages/Sma.cs index 5cedd225..c0294bd0 100644 --- a/lib/averages/Sma.cs +++ b/lib/averages/Sma.cs @@ -4,16 +4,14 @@ public class Sma : AbstractBase { // inherited _index // inherited _value - public readonly int Period; - private CircularBuffer _buffer; + private readonly CircularBuffer _buffer; - public Sma(int period) : base() + public Sma(int period) { if (period < 1) { throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); } - Period = period; WarmupPeriod = period; _buffer = new CircularBuffer(period); Name = "Sma"; @@ -28,11 +26,6 @@ public class Sma : AbstractBase } //inhereted public void Sub(object source, in ValueEventArgs args) - public override void Init() - { - base.Init(); - } - protected override void ManageState(bool isNew) { if (isNew) diff --git a/lib/averages/Smma.cs b/lib/averages/Smma.cs index 1e20301c..0ca8c403 100644 --- a/lib/averages/Smma.cs +++ b/lib/averages/Smma.cs @@ -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) { diff --git a/lib/averages/T3.cs b/lib/averages/T3.cs index 5e7a30df..2924fe95 100644 --- a/lib/averages/T3.cs +++ b/lib/averages/T3.cs @@ -10,7 +10,7 @@ public class T3 : AbstractBase private double _lastEma1, _lastEma2, _lastEma3, _lastEma4, _lastEma5, _lastEma6; private double _p_lastEma1, _p_lastEma2, _p_lastEma3, _p_lastEma4, _p_lastEma5, _p_lastEma6; - public T3(int period, double vfactor = 0.7, bool useSma = true) : base() + public T3(int period, double vfactor = 0.7, bool useSma = true) { if (period < 1) { @@ -48,7 +48,6 @@ public class T3 : AbstractBase public override void Init() { - base.Init(); _lastEma1 = _lastEma2 = _lastEma3 = _lastEma4 = _lastEma5 = _lastEma6 = 0; _buffer1.Clear(); _buffer2.Clear(); diff --git a/lib/averages/Tema.cs b/lib/averages/Tema.cs index 2b12f6ee..4d4abef7 100644 --- a/lib/averages/Tema.cs +++ b/lib/averages/Tema.cs @@ -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) { @@ -58,7 +58,7 @@ public class Tema : AbstractBase { double result, _ema1, _ema2, _ema3; ManageState(Input.IsNew); - + _e = (_e > 1e-10) ? (1 - _k) * _e : 0; double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1; diff --git a/lib/averages/Trima.cs b/lib/averages/Trima.cs index 3abea746..7cfeb715 100644 --- a/lib/averages/Trima.cs +++ b/lib/averages/Trima.cs @@ -2,7 +2,6 @@ namespace QuanTAlib; public class Trima : AbstractBase { - private readonly int _period; private readonly Convolution _convolution; public Trima(int period) @@ -11,8 +10,7 @@ public class Trima : AbstractBase { throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); } - _period = period; - _convolution = new Convolution(GenerateKernel(_period)); + _convolution = new Convolution(GenerateKernel(period)); Name = "Trima"; WarmupPeriod = period; Init(); @@ -32,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]; } diff --git a/lib/averages/Vidya.cs b/lib/averages/Vidya.cs index e421c16c..539aced4 100644 --- a/lib/averages/Vidya.cs +++ b/lib/averages/Vidya.cs @@ -6,24 +6,25 @@ namespace QuanTAlib; public class Vidya : AbstractBase { - private readonly int _shortPeriod; + private readonly int _longPeriod; private readonly double _alpha; private double _lastVIDYA, _p_lastVIDYA; - private CircularBuffer? _shortBuffer; - private CircularBuffer? _longBuffer; + private readonly CircularBuffer? _shortBuffer; + private readonly CircularBuffer? _longBuffer; - public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2) : base() + public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2) { if (shortPeriod < 1) { throw new ArgumentException("Short period must be greater than or equal to 1.", nameof(shortPeriod)); } - _shortPeriod = shortPeriod; _longPeriod = (longPeriod == 0) ? shortPeriod * 4 : longPeriod; _alpha = alpha; WarmupPeriod = _longPeriod; - Name = $"Vidya({_shortPeriod},{_longPeriod})"; + Name = $"Vidya({shortPeriod},{_longPeriod})"; + _shortBuffer = new CircularBuffer(shortPeriod); + _longBuffer = new CircularBuffer(_longPeriod); Init(); } @@ -38,8 +39,6 @@ public class Vidya : AbstractBase { base.Init(); _lastVIDYA = 0; - _shortBuffer = new CircularBuffer(_shortPeriod); - _longBuffer = new CircularBuffer(_longPeriod); } protected override void ManageState(bool isNew) @@ -59,7 +58,7 @@ public class Vidya : AbstractBase protected override double Calculation() { ManageState(Input.IsNew); - + _shortBuffer!.Add(Input.Value, Input.IsNew); _longBuffer!.Add(Input.Value, Input.IsNew); @@ -83,7 +82,7 @@ public class Vidya : AbstractBase } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private double CalculateStdDev(CircularBuffer buffer) + private static double CalculateStdDev(CircularBuffer buffer) { double mean = buffer.Average(); double sumSquaredDiff = buffer.Sum(x => Math.Pow(x - mean, 2)); diff --git a/lib/averages/Zlema.cs b/lib/averages/Zlema.cs index b7115d66..1aae1e16 100644 --- a/lib/averages/Zlema.cs +++ b/lib/averages/Zlema.cs @@ -1,73 +1,72 @@ using System; using System.Runtime.CompilerServices; -namespace QuanTAlib; - -public class Zlema : AbstractBase +namespace QuanTAlib { - private readonly int _period; - private CircularBuffer? _buffer; - private double _alpha; - private int _lag; - private double _lastZLEMA, _p_lastZLEMA; - - public Zlema(int period) : base() + public class Zlema : AbstractBase { - if (period < 1) + private readonly CircularBuffer _buffer; + private readonly int _lag; + private readonly Ema _ema; + private double _lastZLEMA, _p_lastZLEMA; + + public Zlema(int period) { - throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + WarmupPeriod = period; + _lag = (int)(0.5 * (period - 1)); + _buffer = new CircularBuffer(_lag + 1); + _ema = new Ema(period, useSma: false); + Name = $"Zlema({period})"; + Init(); } - _period = period; - WarmupPeriod = period; - _alpha = 2.0 / (_period + 1); - _lag = (_period - 1) / 2; - Name = $"Zlema({_period})"; - Init(); - } - public Zlema(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 = new CircularBuffer(_period); - _lastZLEMA = 0; - } - - protected override void ManageState(bool isNew) - { - if (isNew) + public Zlema(object source, int period) : this(period) { - _lastValidValue = Input.Value; - _index++; - _p_lastZLEMA = _lastZLEMA; + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } - else + + public override void Init() { - _lastZLEMA = _p_lastZLEMA; + base.Init(); + _buffer.Clear(); + _ema.Init(); + _lastZLEMA = 0; + _p_lastZLEMA = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _p_lastZLEMA = _lastZLEMA; + } + else + { + _lastZLEMA = _p_lastZLEMA; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double lagValue = _buffer[Math.Max(0, _buffer.Count - 1 - _lag)]; + double errorCorrection = 2 * Input.Value - lagValue; + double zlema = _ema.Calc(new TValue(errorCorrection, Input.IsNew)).Value; + + _lastZLEMA = zlema; + IsHot = _index >= WarmupPeriod; + + return zlema; } } - - protected override double Calculation() - { - ManageState(Input.IsNew); - - _buffer!.Add(Input.Value, Input.IsNew); - - int lag = Math.Max(Math.Min((int)((_period - 1) * 0.5), _buffer.Count - 1), 0) + 1; - double zlValue = 2 * Input.Value - _buffer[_buffer.Count - lag]; - - // Dynamic alpha factor for index <= period - double k = (_index <= _period) ? (2.0 / (_index + 1)) : _alpha; - double zlema = (zlValue - _lastZLEMA) * k + _lastZLEMA; - - _lastZLEMA = zlema; - IsHot = _index >= WarmupPeriod; - - return zlema; - } -} \ No newline at end of file +} diff --git a/lib/core/AbstractBarBase.cs b/lib/core/AbstractBarBase.cs new file mode 100644 index 00000000..3e46d8d9 --- /dev/null +++ b/lib/core/AbstractBarBase.cs @@ -0,0 +1,97 @@ +namespace QuanTAlib; + +/// +/// Provides a base implementation for financial indicators that work with bar data in the QuanTAlib library. +/// +/// +/// This abstract class implements the iTValue interface and defines common properties +/// and methods used by inheriting indicator types. It handles the basic flow of +/// receiving bar data, performing calculations, and publishing results. +/// +public abstract class AbstractBarBase : ITValue +{ + public DateTime Time { get; set; } + public double Value { get; set; } + public bool IsNew { get; set; } + public bool IsHot { get; set; } + public TBar Input { get; set; } + public String Name { get; set; } = ""; + public int WarmupPeriod { get; set; } + public TValue Tick => new(Time, Value, IsNew, IsHot); + public event ValueSignal Pub = delegate { }; + protected int _index; + protected double _lastValidValue; + protected AbstractBarBase() + { + // Add parameters into constructor if needed + } + + /// + /// Subscribes to bar data updates. + /// + /// The source of the bar data. + /// The event arguments containing the bar data. + public void Sub(object source, in TBarEventArgs args) => Calc(args.Bar); + + /// + /// Initializes the indicator's state. + /// + public virtual void Init() + { + _index = 0; + _lastValidValue = 0; + } + + /// + /// Calculates the indicator value based on the input bar. + /// + /// The input bar data. + /// A TValue containing the calculated result. + public virtual TValue Calc(TBar input) + { + Input = input; + if (double.IsNaN(input.Close) || double.IsInfinity(input.Close)) + { + return Process(new TValue(Time: input.Time, Value: GetLastValid(), IsNew: input.IsNew, IsHot: true)); + } + this.Value = Calculation(); + return Process(new TValue(Time: Input.Time, Value: this.Value, IsNew: Input.IsNew, IsHot: this.IsHot)); + } + + /// + /// Retrieves the last valid calculated value. + /// + /// The last valid value of the indicator. + protected virtual double GetLastValid() + { + return this.Value; + } + + /// + /// Manages the state of the indicator based on whether a new bar is being processed. + /// + /// Indicates whether the current input is a new bar. + protected abstract void ManageState(bool isNew); + + /// + /// Performs the actual calculation of the indicator value. + /// + /// The calculated indicator value. + protected abstract double Calculation(); + + /// + /// Processes the calculated value, updates the indicator's own state, + /// and publishes the result through an event. + /// + /// The calculated TValue to process. + /// The processed TValue. + protected virtual TValue Process(TValue value) + { + this.Time = value.Time; + this.Value = value.Value; + this.IsNew = value.IsNew; + this.IsHot = value.IsHot; + Pub?.Invoke(this, new ValueEventArgs(value)); + return value; + } +} diff --git a/lib/core/abstractBase.cs b/lib/core/abstractBase.cs index 84dfc144..3ac3d973 100644 --- a/lib/core/abstractBase.cs +++ b/lib/core/abstractBase.cs @@ -2,29 +2,32 @@ namespace QuanTAlib; /// /// Provides a base implementation for financial indicators in the QuanTAlib library. -/// This abstract class implements the iTValue interface and defines common properties -/// and methods used by inheriting indicator types. /// -public abstract class AbstractBase : iTValue +/// +/// This abstract class implements the iTValue interface and defines common properties +/// and methods used by inheriting indicator types. It handles the basic flow of +/// receiving data, performing calculations, and publishing results. +/// +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); // Stores the current value of indicator - public event ValueSignal Pub = delegate { }; // Publisher of generated values - - protected int _index; //tracking the position of output + public TValue Tick => new(Time, Value, IsNew, IsHot); + public event ValueSignal Pub = delegate { }; + protected int _index; protected double _lastValidValue; - // other _internal vars defined here protected AbstractBase() - { //add parameters into constructor + { + // Add parameters into constructor if needed } /// @@ -34,36 +37,96 @@ public abstract class AbstractBase : iTValue /// The argument containing the new data point. 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); + + /// + /// Initializes the indicator's state. + /// public virtual void Init() { _index = 0; _lastValidValue = 0; } - /// - /// Calculates the indicator value based on the input; calls specific Calculation() method - /// where implementation is - /// - /// The input value for the calculation. - /// A TValue representing the calculated indicator value. 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 Process(input.Value, input.Time, input.IsNew); + } + public virtual TValue Calc(double value, bool IsNew) + { + Input = new(this.Time, Value: value, IsNew: IsNew, IsHot: false); + Input2 = new(this.Time, double.NaN, false, false); + return Process(Input.Value, Input.Time, Input.IsNew); + } + + public virtual TValue Calc(TBar barInput) + { + BarInput = barInput; + return Process(barInput.Close, barInput.Time, barInput.IsNew); + } + + public virtual TValue Calc(TValue input1, TValue input2) + { + Input = input1; + Input2 = input2; + return Process(input1.Value, input2.Value, input1.Time, input1.IsNew); + } + + public virtual TValue Calc(TBar input1, TBar input2) + { + BarInput = input1; + BarInput2 = input2; + return Process(input1.Close, input2.Close, input1.Time, input1.IsNew); + } + + public virtual TValue Calc(double value1, double value2) + { + DateTime now = DateTime.Now; + Input = new TValue(now, value1, true, true); + Input2 = new TValue(now, value2, true, true); + return Process(value1, value2, now, true); + } + + /// + /// Processes the input values, performs error checking, and calculates the indicator value. + /// + /// The primary input value to process. + /// The timestamp of the input. + /// Indicates if the input is new. + /// A TValue object with the calculated or last valid value. + protected virtual TValue Process(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)); } - protected virtual double GetLastValid() + /// + /// Processes two input values, performs error checking, and calculates the indicator value. + /// + /// The first input value to process. + /// The second input value to process. + /// The timestamp of the input. + /// Indicates if the input is new. + /// A TValue object with the calculated or last valid value. + protected virtual TValue Process(double value1, double value2, DateTime time, bool isNew) { - return this.Value; + 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)); } - protected abstract void ManageState(bool isNew); - protected abstract double Calculation(); - /// /// Processes the calculated value, updates the indicator's own state, /// and publishes the result through an event. @@ -79,4 +142,26 @@ public abstract class AbstractBase : iTValue Pub?.Invoke(this, new ValueEventArgs(value)); return value; } + /// + /// Retrieves the last valid calculated value. + /// + /// The last valid value of the indicator. + protected virtual double GetLastValid() + { + return this.Value; + } + + /// + /// Manages the state of the indicator based on whether a new data point is being processed. + /// + /// Indicates whether the current input is a new data point. + protected abstract void ManageState(bool isNew); + + /// + /// Performs the actual calculation of the indicator value. + /// + /// The calculated indicator value. + protected abstract double Calculation(); + + } diff --git a/lib/core/circularbuffer.cs b/lib/core/circularbuffer.cs index 695c4da3..42740f67 100644 --- a/lib/core/circularbuffer.cs +++ b/lib/core/circularbuffer.cs @@ -4,21 +4,45 @@ using System.Numerics; namespace QuanTAlib; +/// +/// Represents a circular buffer of double values with fixed capacity. +/// +/// +/// This class provides efficient operations for adding, accessing, and manipulating +/// a fixed-size buffer of double values. It uses SIMD operations for improved performance +/// on supported hardware. +/// public class CircularBuffer : IEnumerable { private readonly double[] _buffer; private int _start = 0; private int _size = 0; + /// + /// Gets the maximum number of elements that can be contained in the buffer. + /// public int Capacity { get; } + + /// + /// Gets the number of elements currently contained in the buffer. + /// public int Count => _size; + /// + /// Initializes a new instance of the CircularBuffer class with the specified capacity. + /// + /// The maximum number of elements the buffer can hold. public CircularBuffer(int capacity) { Capacity = capacity; _buffer = GC.AllocateArray(capacity, pinned: true); } + /// + /// Adds an item to the buffer. + /// + /// The item to add to the buffer. + /// Indicates whether the item is a new value or an update to the last added value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(double item, bool isNew = true) { @@ -41,6 +65,11 @@ public class CircularBuffer : IEnumerable } } + /// + /// Gets or sets the element at the specified index. + /// + /// The zero-based index of the element to get or set. + /// The element at the specified index. public double this[Index index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -60,11 +89,15 @@ public class CircularBuffer : IEnumerable } [MethodImpl(MethodImplOptions.NoInlining)] - private static void ThrowArgumentOutOfRangeException() + private static void ThrowArgumentOutOfRangeException(string paramName) { - throw new ArgumentOutOfRangeException("index", "Index is out of range."); + throw new ArgumentOutOfRangeException(paramName, "Index is out of range."); } + /// + /// Gets the newest (most recently added) element in the buffer. + /// + /// The newest element in the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Newest() { @@ -73,6 +106,10 @@ public class CircularBuffer : IEnumerable return _buffer[(_start + _size - 1) % Capacity]; } + /// + /// Gets the oldest element in the buffer. + /// + /// The oldest element in the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Oldest() { @@ -87,10 +124,17 @@ public class CircularBuffer : IEnumerable throw new InvalidOperationException("Buffer is empty."); } + /// + /// Returns an enumerator that iterates through the buffer. + /// + /// An enumerator for the buffer. public Enumerator GetEnumerator() => new(this); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// + /// Represents an enumerator for the CircularBuffer. + /// public struct Enumerator : IEnumerator { private readonly CircularBuffer _buffer; @@ -105,6 +149,10 @@ public class CircularBuffer : IEnumerable _current = default; } + /// + /// Advances the enumerator to the next element of the buffer. + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { @@ -116,18 +164,32 @@ public class CircularBuffer : IEnumerable return true; } + /// + /// Gets the element in the buffer at the current position of the enumerator. + /// public double Current => _current; object IEnumerator.Current => Current; + /// + /// Sets the enumerator to its initial position, which is before the first element in the buffer. + /// public void Reset() { _index = -1; _current = default; } + /// + /// Disposes the enumerator. + /// public void Dispose() { } } + /// + /// Copies the elements of the buffer to an array, starting at a particular array index. + /// + /// The one-dimensional array that is the destination of the elements copied from the buffer. + /// The zero-based index in array at which copying begins. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(double[] destination, int destinationIndex) { @@ -146,6 +208,10 @@ public class CircularBuffer : IEnumerable } } + /// + /// Returns a read-only span over the contents of the buffer. + /// + /// A read-only span over the buffer contents. [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan GetSpan() { @@ -156,17 +222,25 @@ public class CircularBuffer : IEnumerable { return new ReadOnlySpan(_buffer, _start, _size); } - else - { - return new ReadOnlySpan(ToArray()); - } + + return new ReadOnlySpan(ToArray()); } + /// + /// Gets the internal buffer array. + /// public double[] InternalBuffer => _buffer; + /// + /// Returns a read-only span over the entire internal buffer. + /// + /// A read-only span over the entire internal buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan GetInternalSpan() => _buffer.AsSpan(); + /// + /// Removes all elements from the buffer. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { @@ -175,6 +249,10 @@ public class CircularBuffer : IEnumerable _size = 0; } + /// + /// Returns the maximum value in the buffer. + /// + /// The maximum value in the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Max() { @@ -184,6 +262,10 @@ public class CircularBuffer : IEnumerable return MaxSimd(); } + /// + /// Returns the minimum value in the buffer. + /// + /// The minimum value in the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Min() { @@ -193,12 +275,20 @@ public class CircularBuffer : IEnumerable return MinSimd(); } + /// + /// Computes the sum of all values in the buffer. + /// + /// The sum of all values in the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Sum() { return SumSimd(); } + /// + /// Computes the average of all values in the buffer. + /// + /// The average of all values in the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Average() { @@ -289,6 +379,10 @@ public class CircularBuffer : IEnumerable return sum; } + /// + /// Copies the buffer elements to a new array. + /// + /// An array containing copies of the buffer elements. public double[] ToArray() { double[] array = new double[_size]; @@ -296,6 +390,10 @@ public class CircularBuffer : IEnumerable return array; } + /// + /// Performs a parallel operation on the buffer elements. + /// + /// The operation to perform on each partition of the buffer. public void ParallelOperation(Func operation) { const int MinimumPartitionSize = 1024; @@ -326,7 +424,5 @@ public class CircularBuffer : IEnumerable int length = (i == partitionCount - 1) ? _size - start : partitionSize; results[i] = operation(buffer, start, length); }); - } - } \ No newline at end of file diff --git a/lib/core/formatters.cs b/lib/core/formatters.cs index 685ad8e2..9e567ce7 100644 --- a/lib/core/formatters.cs +++ b/lib/core/formatters.cs @@ -9,7 +9,7 @@ public static class Formatters const string pad = "18"; public static void Initialize() { - Formatter.Register((tick, writer) => + Formatter.Register((tick, writer) => { var sb = new StringBuilder(); sb.Append(""); diff --git a/lib/core/tbar.cs b/lib/core/tbar.cs index 9f71e5e4..9b72bc88 100644 --- a/lib/core/tbar.cs +++ b/lib/core/tbar.cs @@ -1,6 +1,6 @@ namespace QuanTAlib; -public interface iTBar +public interface ITBar { DateTime Time { get; } double Open { get; } @@ -11,35 +11,33 @@ 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; - public double High { get; init; } = High; - public double Low { get; init; } = Low; - public double Close { get; init; } = Close; - public double Volume { get; init; } = Volume; - public bool IsNew { get; init; } = IsNew; +public double Open { get; init; } = Open; +public double High { get; init; } = High; +public double Low { get; init; } = Low; +public double Close { get; init; } = Close; +public double Volume { get; init; } = Volume; +public bool IsNew { get; init; } = IsNew; - public double HL2 => (High + Low) * 0.5; - public double OC2 => (Open + Close) * 0.5; - public double OHL3 => (Open + High + Low) / 3; - public double HLC3 => (High + Low + Close) / 3; - public double OHLC4 => (Open + High + Low + Close) * 0.25; - public double HLCC4 => (High + Low + Close + Close) * 0.25; +public double HL2 => (High + Low) * 0.5; +public double OC2 => (Open + Close) * 0.5; +public double OHL3 => (Open + High + Low) / 3; +public double HLC3 => (High + Low + Close) / 3; +public double OHLC4 => (Open + High + Low + Close) * 0.25; +public double HLCC4 => (High + Low + Close + Close) * 0.25; - public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { } - public TBar(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : this(DateTime.UtcNow, Open, High, Low, Close, Volume, IsNew) { } +public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { } +public TBar(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : this(DateTime.UtcNow, Open, High, Low, Close, Volume, IsNew) { } +public TBar(double value) : this(Time: DateTime.UtcNow, Open: value, High: value, Low: value, Close: value, Volume: value, IsNew: true) { } +public TBar(TValue value) : this(Time: value.Time, Open: value.Value, High: value.Value, Low: value.Value, Close: value.Value, Volume: value.Value, IsNew: value.IsNew) { } +public TBar(TBar v) : this(Time: v.Time, Open: v.Open, High: v.High, Low: v.Low, Close: v.Close, Volume: v.Volume, IsNew: true) { } - // when TBar casts to double, it returns its Close - public static implicit operator double(TBar bar) => bar.Close; - public static implicit operator DateTime(TBar tv) => tv.Time; - // castings for sloppy people - a single double injected into a TBar, and a single TValue injected into a TBar - public TBar(double value) : this(Time: DateTime.UtcNow, Open: value, High: value, Low: value, Close: value, Volume: value, IsNew: true) { } - public TBar(TValue value) : this(Time: value.Time, Open: value.Value, High: value.Value, Low: value.Value, Close: value.Value, Volume: value.Value, IsNew: value.IsNew) { } - - public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]"; +public static implicit operator double(TBar bar) => bar.Close; +public static implicit operator DateTime(TBar tv) => tv.Time; +public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]"; } public delegate void BarSignal(object source, in TBarEventArgs args); @@ -70,11 +68,8 @@ public class TBarSeries : List public TBarSeries() { this.Name = "Bar"; - Open = new(); - High = new(); - Low = new(); - Close = new(); - Volume = new(); + (Open, High, Low, Close, Volume) = ([], [], [], [], []); + } public TBarSeries(object source) : this() { @@ -84,7 +79,8 @@ public class TBarSeries : List public new virtual void Add(TBar bar) { - if (bar.IsNew) { base.Add(bar); } else { this[^1] = bar; } + if (bar.IsNew || base.Count == 0) { base.Add(bar); } + else { this[^1] = bar; } Pub?.Invoke(this, new TBarEventArgs(bar)); Open.Add(bar.Time, bar.Open, IsNew: bar.IsNew, IsHot: true); diff --git a/lib/core/tvalue.cs b/lib/core/tvalue.cs index 30a67d29..843768d6 100644 --- a/lib/core/tvalue.cs +++ b/lib/core/tvalue.cs @@ -1,6 +1,6 @@ namespace QuanTAlib; -public interface iTValue +public interface ITValue { DateTime Time { get; } double Value { get; } @@ -8,22 +8,22 @@ 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; - public bool IsNew { get; init; } = IsNew; - public bool IsHot { get; init; } = IsHot; - public DateTime t => Time; - public double v => Value; +public double Value { get; init; } = Value; +public bool IsNew { get; init; } = IsNew; +public bool IsHot { get; init; } = IsHot; +public DateTime t => Time; +public double v => Value; - public TValue() : this(DateTime.UtcNow, 0) { } - public TValue(double value, bool isNew = true, bool isHot = true) : this(DateTime.UtcNow, value, IsNew: isNew, IsHot: isHot) { } - public static implicit operator double(TValue tv) => tv.Value; - public static implicit operator DateTime(TValue tv) => tv.Time; - public static implicit operator TValue(double value) => new TValue(DateTime.UtcNow, value); +public TValue() : this(DateTime.UtcNow, 0) { } +public TValue(double value, bool isNew = true, bool isHot = true) : this(DateTime.UtcNow, value, IsNew: isNew, IsHot: isHot) { } +public static implicit operator double(TValue tv) => tv.Value; +public static implicit operator DateTime(TValue tv) => tv.Time; +public static implicit operator TValue(double value) => new TValue(DateTime.UtcNow, value); - public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}, {Value:F2}, IsNew: {IsNew}, IsHot: {IsHot}]"; +public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}, {Value:F2}, IsNew: {IsNew}, IsHot: {IsHot}]"; } public delegate void ValueSignal(object source, in ValueEventArgs args); @@ -52,12 +52,13 @@ public class TSeries : List 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)); } } @@ -66,7 +67,7 @@ public class TSeries : List public new virtual void Add(TValue tick) { - if (tick.IsNew) { base.Add(tick); } + if (tick.IsNew || base.Count == 0) { base.Add(tick); } else { this[^1] = tick; } Pub?.Invoke(this, new ValueEventArgs(tick)); } diff --git a/lib/errors/Huberloss.cs b/lib/errors/Huberloss.cs new file mode 100644 index 00000000..e2bdac55 --- /dev/null +++ b/lib/errors/Huberloss.cs @@ -0,0 +1,88 @@ +namespace QuanTAlib; + +public class Huberloss : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + private readonly double _delta; + + public Huberloss(int period, double delta = 1.0) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + if (delta <= 0) + { + throw new ArgumentOutOfRangeException(nameof(delta), "Delta must be greater than 0."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + _delta = delta; + Name = $"Huberloss(period={period}, delta={delta})"; + Init(); + } + + public Huberloss(object source, int period, double delta = 1.0) : this(period, delta) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double huberloss = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumLoss = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = actualValues[i] - predictedValues[i]; + double absError = Math.Abs(error); + + if (absError <= _delta) + { + sumLoss += 0.5 * error * error; + } + else + { + sumLoss += _delta * (absError - 0.5 * _delta); + } + } + + huberloss = sumLoss / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return huberloss; + } + +} diff --git a/lib/errors/Mae.cs b/lib/errors/Mae.cs new file mode 100644 index 00000000..f043af52 --- /dev/null +++ b/lib/errors/Mae.cs @@ -0,0 +1,72 @@ +namespace QuanTAlib; + +public class Mae : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Mae(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mae(period={period})"; + Init(); + } + + public Mae(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mae = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumAbsoluteError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + sumAbsoluteError += Math.Abs(actualValues[i] - predictedValues[i]); + } + + mae = sumAbsoluteError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return mae; + } + +} diff --git a/lib/errors/Mapd.cs b/lib/errors/Mapd.cs new file mode 100644 index 00000000..55a15d73 --- /dev/null +++ b/lib/errors/Mapd.cs @@ -0,0 +1,75 @@ +namespace QuanTAlib; + +public class Mapd : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Mapd(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mapd(period={period})"; + Init(); + } + + public Mapd(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mapd = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumAbsolutePercentageDeviation = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + if (actualValues[i] != 0) + { + sumAbsolutePercentageDeviation += Math.Abs((actualValues[i] - predictedValues[i]) / actualValues[i]); + } + } + + mapd = sumAbsolutePercentageDeviation / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return mapd; + } + +} diff --git a/lib/errors/Mape.cs b/lib/errors/Mape.cs new file mode 100644 index 00000000..f19299f9 --- /dev/null +++ b/lib/errors/Mape.cs @@ -0,0 +1,74 @@ +namespace QuanTAlib; + +public class Mape : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Mape(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mape(period={period})"; + Init(); + } + + public Mape(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mape = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumAbsolutePercentageError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + if (actualValues[i] != 0) + { + sumAbsolutePercentageError += Math.Abs((actualValues[i] - predictedValues[i]) / actualValues[i]); + } + } + + mape = sumAbsolutePercentageError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return mape; + } +} diff --git a/lib/errors/Mase.cs b/lib/errors/Mase.cs new file mode 100644 index 00000000..5b486ba7 --- /dev/null +++ b/lib/errors/Mase.cs @@ -0,0 +1,126 @@ +using System; + +namespace QuanTAlib; + +/// +/// Represents the Mean Absolute Scaled Error (MASE) calculation. +/// +public class Mase : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + private readonly CircularBuffer _naiveBuffer; + + /// + /// Initializes a new instance of the Mase class. + /// + /// The period for MASE calculation. + /// Thrown when period is less than 1. + public Mase(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + _naiveBuffer = new CircularBuffer(period); + Name = $"Mase(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mase class with a source object. + /// + /// The source object for event subscription. + /// The period for MASE calculation. + public Mase(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mase instance. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + _naiveBuffer.Clear(); + } + + /// + /// Manages the state of the Mase instance. + /// + /// Indicates if the input is new. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the MASE calculation. + /// + /// The calculated MASE value. + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + if (_actualBuffer.Count > 1) + { + _naiveBuffer.Add(_actualBuffer.GetSpan()[^2], Input.IsNew); + } + + double mase = CalculateMase(); + + IsHot = _index >= WarmupPeriod; + return mase; + } + + private double CalculateMase() + { + if (_actualBuffer.Count <= 1) return 0; + + ReadOnlySpan actualValues = _actualBuffer.GetSpan(); + ReadOnlySpan predictedValues = _predictedBuffer.GetSpan(); + ReadOnlySpan naiveValues = _naiveBuffer.GetSpan(); + + double sumAbsoluteError = CalculateSumAbsoluteError(actualValues, predictedValues); + double _naiveForecastError = CalculateNaiveForecastError(actualValues, naiveValues); + + return _naiveForecastError != 0 ? (sumAbsoluteError / _actualBuffer.Count) / _naiveForecastError : double.PositiveInfinity; + } + + private static double CalculateSumAbsoluteError(ReadOnlySpan actualValues, ReadOnlySpan predictedValues) + { + double sum = 0; + for (int i = 0; i < actualValues.Length; i++) + { + sum += Math.Abs(actualValues[i] - predictedValues[i]); + } + return sum; + } + + private static double CalculateNaiveForecastError(ReadOnlySpan actualValues, ReadOnlySpan naiveValues) + { + double sum = 0; + for (int i = 1; i < actualValues.Length; i++) + { + sum += Math.Abs(actualValues[i] - naiveValues[i - 1]); + } + return sum / (actualValues.Length - 1); + } +} diff --git a/lib/errors/Mda.cs b/lib/errors/Mda.cs new file mode 100644 index 00000000..ba03bced --- /dev/null +++ b/lib/errors/Mda.cs @@ -0,0 +1,73 @@ +namespace QuanTAlib; + +public class Mda : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Mda(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mda(period={period})"; + Init(); + } + + public Mda(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mda = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumDirectionalAccuracy = 0; + for (int i = 1; i < _actualBuffer.Count; i++) + { + double actualDirection = Math.Sign(actualValues[i] - actualValues[i - 1]); + double predictedDirection = Math.Sign(predictedValues[i] - predictedValues[i - 1]); + sumDirectionalAccuracy += (actualDirection == predictedDirection) ? 1 : 0; + } + + mda = sumDirectionalAccuracy / (_actualBuffer.Count - 1); + } + + IsHot = _index >= WarmupPeriod; + return mda; + } +} diff --git a/lib/errors/Me.cs b/lib/errors/Me.cs new file mode 100644 index 00000000..a9118b39 --- /dev/null +++ b/lib/errors/Me.cs @@ -0,0 +1,71 @@ +namespace QuanTAlib; + +public class Me : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Me(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Me(period={period})"; + Init(); + } + + public Me(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double me = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + sumError += actualValues[i] - predictedValues[i]; + } + + me = sumError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return me; + } +} diff --git a/lib/errors/Mpe.cs b/lib/errors/Mpe.cs new file mode 100644 index 00000000..c9fc3a88 --- /dev/null +++ b/lib/errors/Mpe.cs @@ -0,0 +1,74 @@ +namespace QuanTAlib; + +public class Mpe : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Mpe(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mpe(period={period})"; + Init(); + } + + public Mpe(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mpe = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumPercentageError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + if (actualValues[i] != 0) + { + sumPercentageError += (actualValues[i] - predictedValues[i]) / actualValues[i]; + } + } + + mpe = sumPercentageError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return mpe; + } +} diff --git a/lib/errors/Mse.cs b/lib/errors/Mse.cs new file mode 100644 index 00000000..7a3e33b2 --- /dev/null +++ b/lib/errors/Mse.cs @@ -0,0 +1,110 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Squared Error calculator that measures the average of the squares +/// of the differences between actual values and predicted values. +/// +/// +/// The Mse class calculates the Mean Squared Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Mse : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Mse class with the specified period. + /// + /// The period over which to calculate the Mean Squared Error. + /// + /// Thrown when period is less than 1. + /// + public Mse(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mse(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mse class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Squared Error. + public Mse(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mse instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Mse instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Squared Error calculation for the current period. + /// + /// + /// The calculated Mean Squared Error value for the current period. + /// + /// + /// This method calculates the Mean Squared Error using the formula: + /// MSE = sum((actual - predicted)^2) / n + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mse = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = actualValues[i] - predictedValues[i]; + sumSquaredError += error * error; + } + + mse = sumSquaredError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return mse; + } +} diff --git a/lib/errors/Msle.cs b/lib/errors/Msle.cs new file mode 100644 index 00000000..464f4e10 --- /dev/null +++ b/lib/errors/Msle.cs @@ -0,0 +1,74 @@ +namespace QuanTAlib; + +public class Msle : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Msle(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Msle(period={period})"; + Init(); + } + + public Msle(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double msle = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredLogError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double logActual = Math.Log(actualValues[i] + 1); + double logPredicted = Math.Log(predictedValues[i] + 1); + double error = logActual - logPredicted; + sumSquaredLogError += error * error; + } + + msle = sumSquaredLogError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return msle; + } +} diff --git a/lib/errors/Rae.cs b/lib/errors/Rae.cs new file mode 100644 index 00000000..6b25a161 --- /dev/null +++ b/lib/errors/Rae.cs @@ -0,0 +1,73 @@ +namespace QuanTAlib; + +public class Rae : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Rae(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rae(period={period})"; + Init(); + } + + public Rae(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rae = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumAbsoluteError = 0; + double sumAbsoluteActual = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + sumAbsoluteError += Math.Abs(actualValues[i] - predictedValues[i]); + sumAbsoluteActual += Math.Abs(actualValues[i]); + } + + rae = sumAbsoluteError / sumAbsoluteActual; + } + + IsHot = _index >= WarmupPeriod; + return rae; + } +} diff --git a/lib/errors/Rmse.cs b/lib/errors/Rmse.cs new file mode 100644 index 00000000..ba56b346 --- /dev/null +++ b/lib/errors/Rmse.cs @@ -0,0 +1,72 @@ +namespace QuanTAlib; + +public class Rmse : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Rmse(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rmse(period={period})"; + Init(); + } + + public Rmse(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rmse = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = actualValues[i] - predictedValues[i]; + sumSquaredError += error * error; + } + + rmse = Math.Sqrt(sumSquaredError / _actualBuffer.Count); + } + + IsHot = _index >= WarmupPeriod; + return rmse; + } +} diff --git a/lib/errors/Rmsle.cs b/lib/errors/Rmsle.cs new file mode 100644 index 00000000..cfeb4e35 --- /dev/null +++ b/lib/errors/Rmsle.cs @@ -0,0 +1,74 @@ +namespace QuanTAlib; + +public class Rmsle : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Rmsle(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rmsle(period={period})"; + Init(); + } + + public Rmsle(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rmsle = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredLogError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double logActual = Math.Log(actualValues[i] + 1); + double logPredicted = Math.Log(predictedValues[i] + 1); + double error = logActual - logPredicted; + sumSquaredLogError += error * error; + } + + rmsle = Math.Sqrt(sumSquaredLogError / _actualBuffer.Count); + } + + IsHot = _index >= WarmupPeriod; + return rmsle; + } +} diff --git a/lib/errors/Rse.cs b/lib/errors/Rse.cs new file mode 100644 index 00000000..dc26bc95 --- /dev/null +++ b/lib/errors/Rse.cs @@ -0,0 +1,77 @@ +namespace QuanTAlib; + +public class Rse : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Rse(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rse(period={period})"; + Init(); + } + + public Rse(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rse = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredError = 0; + double sumSquaredActual = 0; + double meanActual = actualValues.Average(); + + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = actualValues[i] - predictedValues[i]; + sumSquaredError += error * error; + double deviation = actualValues[i] - meanActual; + sumSquaredActual += deviation * deviation; + } + + rse = Math.Sqrt(sumSquaredError / sumSquaredActual); + } + + IsHot = _index >= WarmupPeriod; + return rse; + } +} diff --git a/lib/errors/Rsquared.cs b/lib/errors/Rsquared.cs new file mode 100644 index 00000000..e6213cb1 --- /dev/null +++ b/lib/errors/Rsquared.cs @@ -0,0 +1,80 @@ +namespace QuanTAlib; + +public class Rsquared : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Rsquared(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rsquared(period={period})"; + Init(); + } + + public Rsquared(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rsquared = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double meanActual = actualValues.Average(); + double sumSquaredTotal = 0; + double sumSquaredResidual = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + double deviation = actualValues[i] - meanActual; + sumSquaredTotal += deviation * deviation; + double error = actualValues[i] - predictedValues[i]; + sumSquaredResidual += error * error; + } + + if (sumSquaredTotal != 0) + { + rsquared = 1 - (sumSquaredResidual / sumSquaredTotal); + } + } + + IsHot = _index >= WarmupPeriod; + return rsquared; + } +} diff --git a/lib/errors/Smape.cs b/lib/errors/Smape.cs new file mode 100644 index 00000000..869ac820 --- /dev/null +++ b/lib/errors/Smape.cs @@ -0,0 +1,78 @@ +namespace QuanTAlib; + +public class Smape : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + public Smape(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Smape(period={period})"; + Init(); + } + + public Smape(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double smape = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSymmetricAbsolutePercentageError = 0; + int validCount = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + double denominator = Math.Abs(actualValues[i]) + Math.Abs(predictedValues[i]); + if (denominator != 0) + { + sumSymmetricAbsolutePercentageError += Math.Abs(actualValues[i] - predictedValues[i]) / denominator; + validCount++; + } + } + + smape = validCount > 0 ? (200 * sumSymmetricAbsolutePercentageError / validCount) : 0; + } + + IsHot = _index >= WarmupPeriod; + return smape; + } +} diff --git a/lib/feeds/GbmFeed.cs b/lib/feeds/GbmFeed.cs index dfcf2365..566ee8db 100644 --- a/lib/feeds/GbmFeed.cs +++ b/lib/feeds/GbmFeed.cs @@ -1,70 +1,76 @@ -using System.CommandLine.Rendering.Views; +using System.Security.Cryptography; namespace QuanTAlib; public class GbmFeed : TBarSeries { - private readonly double _mu, _sigma; - private readonly Random _random; - private double _lastClose, _lastHigh, _lastLow; + private readonly double _mu, _sigma; + private readonly RandomNumberGenerator _rng; + private double _lastClose; - public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2) : base() - { - _lastClose = _lastHigh = _lastLow = initialPrice; - _mu = mu; - _sigma = sigma; - _random = new Random((int)DateTime.Now.Ticks); - this.Name = $"GBM({_sigma:F2})"; - } + public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2) + { + _lastClose = initialPrice; + _mu = mu; + _sigma = sigma; + _rng = RandomNumberGenerator.Create(); + this.Name = $"GBM({_sigma:F2})"; + } - public void Add(bool isNew = true) => Add(time: DateTime.Now, isNew: isNew); - public void Add(DateTime time, bool isNew = true) => base.Add(Generate(time, isNew)); - public void Add(int count) - { - DateTime startTime = DateTime.UtcNow - TimeSpan.FromHours(count); - TBar lastBar = new(); - for (int i = 0; i < count; i++) - { - Add(startTime, true); - Add(startTime, false); - Add(startTime, false); - startTime = startTime.AddHours(1); - } - } + public void Add(bool isNew = true) => Add(time: DateTime.Now, isNew: isNew); + public void Add(DateTime time, bool isNew = true) => base.Add(Generate(time, isNew)); + public void Add(int count) + { + DateTime startTime = DateTime.UtcNow - TimeSpan.FromHours(count); + for (int i = 0; i < count; i++) + { + Add(startTime, isNew: true); + startTime = startTime.AddHours(1); + } + } - public TBar Generate(DateTime time, bool isNew = true) - { - double dt = 1.0 / 252; - double drift = (_mu - 0.5 * _sigma * _sigma) * dt; - double diffusion = _sigma * Math.Sqrt(dt) * GenerateNormalRandom(); - double newClose = _lastClose * Math.Exp(drift + diffusion); + public TBar Generate(DateTime time, bool isNew = true) + { + double dt = 1.0 / 252; + double drift = (_mu - 0.5 * _sigma * _sigma) * dt; + double diffusion = _sigma * Math.Sqrt(dt) * GenerateNormalRandom(); - double open = _lastClose; - double high = Math.Max(_lastHigh, Math.Max(open, newClose) * (1 + _random.NextDouble() * 0.01)); - double low = Math.Min(_lastLow, Math.Min(open, newClose) * (1 - _random.NextDouble() * 0.01)); - double volume = 1000 + _random.NextDouble() * 1000; + double open = _lastClose; + double close = open * Math.Exp(drift + diffusion); - if (isNew) - { - _lastClose = newClose; - } - else - { - high = Math.Max(_lastHigh, high); - low = Math.Min(_lastLow, low); - } - _lastHigh = high; - _lastLow = low; + // Generate intra-bar price movements + double maxMove = Math.Abs(close - open) * 1.5; // Allow for some extra movement within the bar + double high = Math.Max(open, close) + maxMove * GenerateRandomDouble(); + double low = Math.Min(open, close) - maxMove * GenerateRandomDouble(); - TBar bar = new(time, open, high, low, newClose, volume, isNew); - return bar; - } + // Ensure high is always greater than or equal to both open and close + high = Math.Max(high, Math.Max(open, close)); - private double GenerateNormalRandom() - { - // Box-Muller transform to generate standard normal random variable - double u1 = 1.0 - _random.NextDouble(); // Uniform(0,1] random doubles - double u2 = 1.0 - _random.NextDouble(); - return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); - } -} \ No newline at end of file + // Ensure low is always less than or equal to both open and close + low = Math.Min(low, Math.Min(open, close)); + + double volume = 1000 + GenerateRandomDouble() * 1000; + + if (isNew) + { + _lastClose = close; + } + + return new TBar(time, open, high, low, close, volume, isNew); + } + + private double GenerateNormalRandom() + { + // Box-Muller transform to generate standard normal random variable + double u1 = 1.0 - GenerateRandomDouble(); // Uniform(0,1] random doubles + double u2 = 1.0 - GenerateRandomDouble(); + return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + } + + private double GenerateRandomDouble() + { + byte[] bytes = new byte[8]; + _rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue; + } +} diff --git a/lib/oscillators/Cmo.cs b/lib/oscillators/Cmo.cs new file mode 100644 index 00000000..0b8d92c5 --- /dev/null +++ b/lib/oscillators/Cmo.cs @@ -0,0 +1,70 @@ +namespace QuanTAlib; + +/// +/// Represents a Chande Momentum Oscillator (CMO) calculator. +/// +public class Cmo : AbstractBase +{ + private readonly CircularBuffer _sumH; + private readonly CircularBuffer _sumL; + private double _prevValue, _p_prevValue; + + public Cmo(int period) + { + if (period < 1) + throw new ArgumentOutOfRangeException(nameof(period)); + _sumH = new(period); + _sumL = new(period); + + WarmupPeriod = period+1; + Name = $"CMO({period})"; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + _p_prevValue = _prevValue; + } + else + { + _prevValue = _p_prevValue; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + if (_index == 0) + { + _prevValue = Input.Value; + } + + double diff = Input.Value - _prevValue; + _prevValue = Input.Value; + + if (diff > 0) + { + _sumH.Add(diff, Input.IsNew); + _sumL.Add(0, Input.IsNew); + } + else + { + _sumH.Add(0, Input.IsNew); + _sumL.Add(-diff, Input.IsNew); + + } + + // Calculate sums for the specified period only + double sumH = _sumH.Sum(); + double sumL = _sumL.Sum(); + double divisor = sumH + sumL; + + return (Math.Abs(divisor) > double.Epsilon) ? + 100.0 * ((sumH - sumL) / divisor) : + 0.0; + } +} + diff --git a/lib/quantalib.csproj b/lib/quantalib.csproj index 4c228dec..3f706bc7 100644 --- a/lib/quantalib.csproj +++ b/lib/quantalib.csproj @@ -1,57 +1,55 @@ - + - QuanTAlib - Library of TA Calculations, Charts and Strategies for Quantower - Quantitative Technical Analysis Library in C# for Quantower + QuanTAlib + Library of TA Calculations, Charts and Strategies for Quantower + Quantitative Technical Analysis Library in C# for Quantower git - https://github.com/mihakralj/QuanTAlib - true - Miha Kralj - Miha Kralj - Apache-2.0 - readme.md - net8.0 - enable - preview - enable - false - en-US - QuanTAlib - QuanTAlib + https://github.com/mihakralj/QuanTAlib + true + Miha Kralj + Miha Kralj + readme.md + QuanTAlib + QuanTAlib + 0.0.0.0 True AnyCPU - False + False full True True - + QuanTAlib2.png + readme.md + Apache-2.0 + Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo; AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex; Quantitative;Historical;Quotes; - $(NoWarn);NU5104 - false - false - false + QuanTAlib2.png + https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png + True + false + $(NoWarn);NU1903;NU5104 - - QuanTAlib2.png - https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png - True - + - - - + + + + + + + - ..\.github\TradingPlatform.BusinessLayer.dll + ..\.github\TradingPlatform.BusinessLayer.dll TradingPlatform.BusinessLayer.xml - \ No newline at end of file + diff --git a/lib/statistics/Curvature.cs b/lib/statistics/Curvature.cs new file mode 100644 index 00000000..59330724 --- /dev/null +++ b/lib/statistics/Curvature.cs @@ -0,0 +1,177 @@ +namespace QuanTAlib; + +/// +/// Calculates the rate of change of the slope over a specified period. +/// Provides insights into trend acceleration or deceleration. +/// +/// +/// Curvature is a second-order derivative that measures how quickly the slope (first-order derivative) is changing. +/// Positive curvature indicates accelerating uptrends or decelerating downtrends. +/// Negative curvature indicates decelerating uptrends or accelerating downtrends. +/// This indicator can be useful for identifying potential trend reversals or confirming trend strength. +/// +public class Curvature : AbstractBase +{ + private readonly int _period; + private readonly Slope _slopeCalculator; + private readonly CircularBuffer _slopeBuffer; + + /// + /// Gets the y-intercept of the curvature line. + /// + public double? Intercept { get; private set; } + + /// + /// Gets the standard deviation of the slope values used in the curvature calculation. + /// + public double? StdDev { get; private set; } + + /// + /// Gets the R-squared value, indicating the goodness of fit of the curvature line. + /// + public double? RSquared { get; private set; } + + /// + /// Gets the last calculated point on the curvature line. + /// + public double? Line { get; private set; } + + /// + /// Initializes a new instance of the Curvature class. + /// + /// The number of data points to consider for calculation. + /// + /// Thrown when the period is 2 or less. + /// + public Curvature(int period) + { + if (period <= 2) + { + throw new ArgumentOutOfRangeException(nameof(period), period, + "Period must be greater than 2 for Curvature calculation."); + } + _period = period; + WarmupPeriod = period * 2 - 1; // Number of points needed for period number of slopes + _slopeCalculator = new Slope(period); + _slopeBuffer = new CircularBuffer(period); + Name = $"Curvature(period={period})"; + + Init(); + } + + /// + /// Initializes a new instance of the Curvature class with a data source. + /// + /// The source object that publishes data. + /// The number of data points to consider. + public Curvature(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Resets the Curvature indicator to its initial state. + /// + public override void Init() + { + base.Init(); + _slopeBuffer.Clear(); + Intercept = null; + StdDev = null; + RSquared = null; + Line = null; + } + + /// + /// Manages the state of the indicator. + /// + /// Indicates if the current data point is new. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the curvature calculation. + /// + /// + /// The calculated curvature value. Positive for increasing slope, negative for decreasing. + /// + /// + /// Uses least squares method for optimal calculation. Also computes additional statistics + /// such as Intercept, Standard Deviation, R-Squared, and Line value. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + var slopeResult = _slopeCalculator.Calc(Input); + _slopeBuffer.Add(slopeResult.Value, Input.IsNew); + + double curvature = 0; + + if (_slopeBuffer.Count < 2) + { + return curvature; // Not enough points for calculation + } + + int count = Math.Min(_slopeBuffer.Count, _period); + var slopes = _slopeBuffer.GetSpan().ToArray(); + + // Calculate averages + double sumX = 0, sumY = 0; + for (int i = 0; i < count; i++) + { + sumX += i + 1; + sumY += slopes[i]; + } + double avgX = sumX / count; + double avgY = sumY / count; + + // Least squares method + double sumSqX = 0, sumSqY = 0, sumSqXY = 0; + for (int i = 0; i < count; i++) + { + double devX = (i + 1) - avgX; + double devY = slopes[i] - avgY; + sumSqX += devX * devX; + sumSqY += devY * devY; + sumSqXY += devX * devY; + } + + if (sumSqX > 0) + { + curvature = sumSqXY / sumSqX; + Intercept = avgY - (curvature * avgX); + + // Calculate Standard Deviation and R-Squared + double stdDevX = Math.Sqrt(sumSqX / count); + double stdDevY = Math.Sqrt(sumSqY / count); + StdDev = stdDevY; + + if (stdDevX * stdDevY != 0) + { + double r = sumSqXY / (stdDevX * stdDevY) / count; + RSquared = r * r; + } + + // Calculate last Line value (y = mx + b) + Line = (curvature * count) + Intercept; + } + else + { + Intercept = null; + StdDev = null; + RSquared = null; + Line = null; + } + + IsHot = _slopeBuffer.Count == _period; + return curvature; + } +} diff --git a/lib/statistics/Entropy.cs b/lib/statistics/Entropy.cs index 67b86482..7803f7e4 100644 --- a/lib/statistics/Entropy.cs +++ b/lib/statistics/Entropy.cs @@ -1,39 +1,71 @@ namespace QuanTAlib; -using System; -using System.Linq; - -// Shannon's Entropy calculation +/// +/// Measures the unpredictability of data using Shannon's Entropy. +/// Provides insights into the randomness or information content of the time series. +/// +/// +/// Shannon's Entropy quantifies the average amount of information contained in a message. +/// In the context of time series analysis, it can be used to: +/// - Detect regime changes or structural breaks in the data. +/// - Assess the complexity or predictability of price movements. +/// - Identify periods of high uncertainty or information flow in the market. +/// The entropy value is normalized between 0 and 1, where 1 indicates maximum randomness +/// and 0 indicates perfect predictability. +/// public class Entropy : AbstractBase { - public readonly int Period; - private CircularBuffer _buffer; + /// + /// The number of data points to consider for the entropy calculation. + /// + private readonly int Period; + private readonly CircularBuffer _buffer; - public Entropy(int period) : base() + /// + /// Initializes a new instance of the Entropy class. + /// + /// The number of data points to consider for calculation. + /// + /// Thrown when the period is less than 2. + /// + public Entropy(int period) { if (period < 2) { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for entropy calculation."); + throw new ArgumentOutOfRangeException(nameof(period), + "Period must be greater than or equal to 2 for entropy calculation."); } Period = period; - WarmupPeriod = 2; + WarmupPeriod = 2; // Minimum number of points needed for entropy calculation _buffer = new CircularBuffer(period); Name = $"Entropy(period={period})"; Init(); } + /// + /// Initializes a new instance of the Entropy class with a data source. + /// + /// The source object that publishes data. + /// The number of data points to consider. public Entropy(object source, int period) : this(period) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Resets the Entropy indicator to its initial state. + /// public override void Init() { base.Init(); _buffer.Clear(); } + /// + /// Manages the state of the indicator. + /// + /// Indicates if the current data point is new. protected override void ManageState(bool isNew) { if (isNew) @@ -43,6 +75,17 @@ public class Entropy : AbstractBase } } + /// + /// Performs the entropy calculation. + /// + /// + /// The calculated entropy value, normalized between 0 and 1. + /// 1 indicates maximum randomness, 0 indicates perfect predictability. + /// + /// + /// Uses Shannon's Entropy formula and normalizes the result based on the + /// number of unique values in the current period. + /// protected override double Calculation() { ManageState(Input.IsNew); @@ -70,11 +113,13 @@ public class Entropy : AbstractBase double maxEntropy = Math.Log2(uniqueValueCount); entropy = entropy == 0 ? 1 : entropy / maxEntropy; - } - else { entropy = 1; } + else + { + entropy = 1; // Default to maximum entropy when insufficient data + } IsHot = _buffer.Count >= Period; return entropy; } -} \ No newline at end of file +} diff --git a/lib/statistics/Kurtosis.cs b/lib/statistics/Kurtosis.cs index 1a5be948..cd32c92f 100644 --- a/lib/statistics/Kurtosis.cs +++ b/lib/statistics/Kurtosis.cs @@ -1,16 +1,44 @@ namespace QuanTAlib; -// Excess kurtosis calculated with Sheskin Algorithm +/// +/// Calculates excess kurtosis using the Sheskin Algorithm. +/// Measures the "tailedness" of the probability distribution of a real-valued random variable. +/// +/// +/// Kurtosis is a measure of the combined weight of a distribution's tails relative to the center of the distribution. +/// In financial time series analysis, kurtosis can provide insights into: +/// - The frequency and magnitude of extreme returns. +/// - The potential for outliers or "black swan" events. +/// - The shape of the return distribution compared to a normal distribution. +/// +/// Interpretation: +/// - Excess kurtosis > 0: Heavy-tailed distribution (more extreme values than a normal distribution) +/// - Excess kurtosis = 0: Normal distribution +/// - Excess kurtosis < 0: Light-tailed distribution (fewer extreme values than a normal distribution) +/// +/// High kurtosis in financial returns may indicate a higher risk of extreme events. +/// public class Kurtosis : AbstractBase { - public readonly int Period; - private CircularBuffer _buffer; + /// + /// The number of data points to consider for the kurtosis calculation. + /// + private readonly int Period; + private readonly CircularBuffer _buffer; - public Kurtosis(int period) : base() + /// + /// Initializes a new instance of the Kurtosis class. + /// + /// The number of data points to consider for calculation. + /// + /// Thrown when the period is less than 4. + /// + public Kurtosis(int period) { if (period < 4) { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 4 for kurtosis calculation."); + throw new ArgumentOutOfRangeException(nameof(period), + "Period must be greater than or equal to 4 for kurtosis calculation."); } Period = period; WarmupPeriod = Period - 1; @@ -19,18 +47,30 @@ public class Kurtosis : AbstractBase Init(); } + /// + /// Initializes a new instance of the Kurtosis class with a data source. + /// + /// The source object that publishes data. + /// The number of data points to consider. public Kurtosis(object source, int period) : this(period) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Resets the Kurtosis indicator to its initial state. + /// public override void Init() { base.Init(); _buffer.Clear(); } + /// + /// Manages the state of the indicator. + /// + /// Indicates if the current data point is new. protected override void ManageState(bool isNew) { if (isNew) @@ -40,6 +80,22 @@ public class Kurtosis : AbstractBase } } + /// + /// Performs the kurtosis calculation. + /// + /// + /// The calculated excess kurtosis. Positive for heavy-tailed distributions, + /// negative for light-tailed distributions. + /// + /// + /// Uses the Sheskin Algorithm for kurtosis calculation. + /// Requires at least 4 data points for a valid calculation. + /// + /// Interpretation of results: + /// - Positive values indicate a distribution with heavier tails and a higher peak compared to a normal distribution. + /// - Negative values indicate a distribution with lighter tails and a lower peak compared to a normal distribution. + /// - A value close to 0 suggests a distribution similar to a normal distribution in terms of tailedness. + /// protected override double Calculation() { ManageState(Input.IsNew); @@ -65,7 +121,7 @@ public class Kurtosis : AbstractBase double variance = s2 / (n - 1); - // Using the Sheskin Algorithm for kurtosis + // Sheskin Algorithm kurtosis = (n * (n + 1) * s4) / (variance * variance * (n - 3) * (n - 1) * (n - 2)) - (3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3))); } diff --git a/lib/statistics/Max.cs b/lib/statistics/Max.cs index 2eccaaaf..2f110a03 100644 --- a/lib/statistics/Max.cs +++ b/lib/statistics/Max.cs @@ -1,80 +1,152 @@ -using System; +namespace QuanTAlib; -namespace QuanTAlib +/// +/// Calculates the maximum value over a specified period, with an optional decay factor. +/// Useful for tracking the highest point in a time series with the ability to gradually forget old peaks. +/// +/// +/// The Max indicator is particularly useful in financial analysis for: +/// - Identifying resistance levels in price charts. +/// - Tracking the highest price over a given period. +/// - Implementing trailing stop-loss strategies. +/// +/// The decay factor allows the indicator to adapt to changing market conditions by +/// gradually reducing the influence of older maximum values. +/// +public class Max : AbstractBase { - public class Max : AbstractBase + /// + /// The number of data points to consider for the maximum calculation. + /// + private readonly int Period; + + /// + /// Circular buffer to store the most recent data points. + /// + private readonly CircularBuffer _buffer; + + /// + /// The half-life decay factor used to gradually forget old peaks. + /// + private readonly double _halfLife; + + /// + /// The current maximum value. + /// + private double _currentMax; + + /// + /// The previous maximum value. + /// + private double _p_currentMax; + + /// + /// The number of periods since a new maximum was set. + /// + private int _timeSinceNewMax; + + /// + /// The previous value of _timeSinceNewMax. + /// + private int _p_timeSinceNewMax; + + /// + /// Initializes a new instance of the Max class. + /// + /// The number of data points to consider. Must be at least 1. + /// Half-life decay factor. Set to 0 for no decay, higher for faster forgetting of old peaks. Default is 0. + /// + /// Thrown when the period is less than 1 or decay is negative. + /// + public Max(int period, double decay = 0) { - public readonly int Period; - private CircularBuffer _buffer; - private readonly double _halfLife; - private double _currentMax, _p_currentMax; - private int _timeSinceNewMax, _p_timeSinceNewMax; - - public Max(int period, double decay = 0) : base() + if (period < 1) { - if (period < 1) - { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); - } - if (decay < 0) - { - throw new ArgumentOutOfRangeException(nameof(decay), "Half-life must be non-negative."); - } - Period = period; - WarmupPeriod = 0; - _buffer = new CircularBuffer(period); - _halfLife = decay * 0.1; - Name = $"Max(period={period}, halfLife={decay:F2})"; - Init(); + throw new ArgumentOutOfRangeException(nameof(period), + "Period must be greater than or equal to 1."); } - - public Max(object source, int period, double decay = 0) : this(period, decay) + if (decay < 0) { - var pubEvent = source.GetType().GetEvent("Pub"); - pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + throw new ArgumentOutOfRangeException(nameof(decay), + "Half-life must be non-negative."); } + Period = period; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + _halfLife = decay * 0.1; + Name = $"Max(period={period}, halfLife={decay:F2})"; + Init(); + } - public override void Init() + /// + /// Initializes a new instance of the Max class with a data source. + /// + /// The source object that publishes data. + /// The number of data points to consider. + /// Half-life decay factor. Default is 0. + public Max(object source, int period, double decay = 0) : this(period, decay) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Resets the Max indicator to its initial state. + /// + public override void Init() + { + base.Init(); + _currentMax = double.MinValue; + _timeSinceNewMax = 0; + } + + /// + /// Manages the state of the indicator. + /// + /// Indicates if the current data point is new. + protected override void ManageState(bool isNew) + { + if (isNew) { - base.Init(); - _currentMax = double.MinValue; + _p_currentMax = _currentMax; + _lastValidValue = Input.Value; + _index++; + _timeSinceNewMax++; + _p_timeSinceNewMax = _timeSinceNewMax; + } + else + { + _currentMax = _p_currentMax; + _timeSinceNewMax = _p_timeSinceNewMax; + } + } + + /// + /// Performs the max calculation. + /// + /// + /// The current maximum value, potentially adjusted by the decay factor. + /// + /// + /// Uses a decay factor to gradually forget old peaks. The max value is always + /// capped by the highest value in the current period. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + if (Input.Value >= _currentMax) + { + _currentMax = Input.Value; _timeSinceNewMax = 0; } - protected override void ManageState(bool isNew) - { - if (isNew) - { - _p_currentMax = _currentMax; - _lastValidValue = Input.Value; - _index++; - _timeSinceNewMax++; - _p_timeSinceNewMax = _timeSinceNewMax; - } - else - { - _currentMax = _p_currentMax; - _timeSinceNewMax = _p_timeSinceNewMax; - } - } + double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period); + _currentMax -= decayRate * (_currentMax - _buffer.Average()); + _currentMax = Math.Min(_currentMax, _buffer.Max()); - protected override double Calculation() - { - ManageState(Input.IsNew); - _buffer.Add(Input.Value, Input.IsNew); - - if (Input.Value >= _currentMax) - { - _currentMax = Input.Value; - _timeSinceNewMax = 0; - } - - double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period); - _currentMax = _currentMax - decayRate * (_currentMax - _buffer.Average()); - _currentMax = Math.Min(_currentMax, _buffer.Max()); - - IsHot = true; - return _currentMax; - } + IsHot = true; + return _currentMax; } -} \ No newline at end of file +} diff --git a/lib/statistics/Median.cs b/lib/statistics/Median.cs index 9adb1ed5..c999099e 100644 --- a/lib/statistics/Median.cs +++ b/lib/statistics/Median.cs @@ -1,74 +1,111 @@ -using System; -using System.Linq; +namespace QuanTAlib; -namespace QuanTAlib +/// +/// Calculates the median value over a specified period. +/// Provides a measure of central tendency that is robust to outliers. +/// +/// +/// The Median indicator is particularly useful in financial analysis for: +/// - Providing a robust measure of central tendency that is less affected by extreme values than the mean. +/// - Identifying the middle value in a dataset, which can be helpful in understanding price distributions. +/// - Serving as a basis for other indicators or trading strategies that require a stable reference point. +/// +/// Unlike the mean, the median is not influenced by extreme outliers, making it valuable +/// in markets with occasional large price swings or in the presence of data anomalies. +/// +public class Median : AbstractBase { - public class Median : AbstractBase + /// + /// The number of data points to consider for the median calculation. + /// + private readonly int Period; + private readonly CircularBuffer _buffer; + + /// + /// Initializes a new instance of the Median class. + /// + /// The number of data points to consider. Must be at least 1. + /// + /// Thrown when the period is less than 1. + /// + public Median(int period) { - public readonly int Period; - private CircularBuffer _buffer; - - public Median(int period) : base() + if (period < 1) { - if (period < 1) - { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); - } - Period = period; - WarmupPeriod = period; - _buffer = new CircularBuffer(period); - Name = $"Median(period={period})"; - Init(); + throw new ArgumentOutOfRangeException(nameof(period), + "Period must be greater than or equal to 1."); } + Period = period; + WarmupPeriod = period; + _buffer = new CircularBuffer(period); + Name = $"Median(period={period})"; + Init(); + } - public Median(object source, int period) : this(period) + /// + /// Initializes a new instance of the Median class with a data source. + /// + /// The source object that publishes data. + /// The number of data points to consider. + public Median(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Resets the Median indicator to its initial state. + /// + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + /// + /// Manages the state of the indicator. + /// + /// Indicates if the current data point is new. + 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(); - } - - protected override void ManageState(bool isNew) - { - if (isNew) - { - _lastValidValue = Input.Value; - _index++; - } - } - - protected override double Calculation() - { - ManageState(Input.IsNew); - _buffer.Add(Input.Value, Input.IsNew); - - double median; - if (_index >= Period) - { - var sortedValues = _buffer.GetSpan().ToArray(); - Array.Sort(sortedValues); - int middleIndex = sortedValues.Length / 2; - - if (sortedValues.Length % 2 == 0) - { - median = (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0; - } - else - { - median = sortedValues[middleIndex]; - } - } - else - { - median = _buffer.Average(); // Use average until we have enough data points - } - - IsHot = _index >= WarmupPeriod; - return median; + _lastValidValue = Input.Value; + _index++; } } -} \ No newline at end of file + + /// + /// Performs the median calculation. + /// + /// + /// The current median value of the dataset. + /// + /// + /// Uses a sorting approach to find the median. If there's not enough data, + /// it uses the average as a temporary measure. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + double median; + if (_index >= Period) + { + var sortedValues = _buffer.GetSpan().ToArray(); + Array.Sort(sortedValues); + int middleIndex = sortedValues.Length / 2; + + median = (sortedValues.Length % 2 == 0) ? (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0 : sortedValues[middleIndex]; + } + else + { + // Not enough data, use average as temporary measure + median = _buffer.Average(); + } + + IsHot = _index >= WarmupPeriod; + return median; + } +} diff --git a/lib/statistics/Min.cs b/lib/statistics/Min.cs index eb842a16..5832bdc4 100644 --- a/lib/statistics/Min.cs +++ b/lib/statistics/Min.cs @@ -1,80 +1,150 @@ -using System; +namespace QuanTAlib; -namespace QuanTAlib +/// +/// Represents a minimum value calculator with optional decay over a specified period. +/// This class calculates the minimum value within a given period, with the ability to +/// apply a decay factor to give more weight to recent values. +/// +/// +/// The Min class uses a circular buffer to store values and calculates the minimum +/// efficiently. It also implements a decay mechanism to adjust the minimum value over +/// time, allowing for a more responsive indicator in changing market conditions. +/// +/// The decay factor allows the indicator to "forget" old minimum values gradually, +/// which can be useful in adapting to new price trends or market regimes. +/// +public class Min : AbstractBase { - public class Min : AbstractBase + /// + /// The number of data points to consider for the minimum calculation. + /// + private readonly int Period; + + /// + /// Circular buffer to store the most recent data points. + /// + private readonly CircularBuffer _buffer; + + /// + /// The half-life decay factor used to gradually forget old minimums. + /// + private readonly double _halfLife; + + /// + /// The current minimum value. + /// + private double _currentMin; + + /// + /// The previous minimum value. + /// + private double _p_currentMin; + + /// + /// The number of periods since a new minimum was set. + /// + private int _timeSinceNewMin; + + /// + /// The previous value of _timeSinceNewMin. + /// + private int _p_timeSinceNewMin; + + /// + /// Initializes a new instance of the Min class with the specified period and decay. + /// + /// The period over which to calculate the minimum value. + /// The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay). + /// + /// Thrown when period is less than 1 or decay is negative. + /// + public Min(int period, double decay = 0) { - public readonly int Period; - private CircularBuffer _buffer; - private readonly double _halfLife; - private double _currentMin, _p_currentMin; - private int _timeSinceNewMin, _p_timeSinceNewMin; - - public Min(int period, double decay = 0) : base() + if (period < 1) { - if (period < 1) - { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); - } - if (decay < 0) - { - throw new ArgumentOutOfRangeException(nameof(decay), "Half-life must be non-negative."); - } - Period = period; - WarmupPeriod = 0; - _buffer = new CircularBuffer(period); - _halfLife = decay * 0.1; - Name = $"Min(period={period}, halfLife={decay:F2})"; - Init(); + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); } - - public Min(object source, int period, double decay = 0) : this(period, decay) + if (decay < 0) { - var pubEvent = source.GetType().GetEvent("Pub"); - pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + throw new ArgumentOutOfRangeException(nameof(decay), "Half-life must be non-negative."); } + Period = period; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + _halfLife = decay * 0.1; + Name = $"Min(period={period}, halfLife={decay:F2})"; + Init(); + } - public override void Init() + /// + /// Initializes a new instance of the Min class with the specified source, period, and decay. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the minimum value. + /// The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay). + public Min(object source, int period, double decay = 0) : this(period, decay) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Min instance by setting initial values. + /// + public override void Init() + { + base.Init(); + _currentMin = double.MaxValue; + _timeSinceNewMin = 0; + } + + /// + /// Manages the state of the Min instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + protected override void ManageState(bool isNew) + { + if (isNew) { - base.Init(); - _currentMin = double.MaxValue; + _p_currentMin = _currentMin; + _lastValidValue = Input.Value; + _index++; + _timeSinceNewMin++; + _p_timeSinceNewMin = _timeSinceNewMin; + } + else + { + _currentMin = _p_currentMin; + _timeSinceNewMin = _p_timeSinceNewMin; + } + } + + /// + /// Performs the minimum value calculation with decay. + /// + /// The calculated minimum value for the current period. + /// + /// This method updates the current minimum value based on the input, applies the decay + /// factor, and ensures the result is not lower than the actual minimum in the buffer. + /// The decay rate is calculated using an exponential function based on the time since + /// the last new minimum and the specified half-life. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + if (Input.Value <= _currentMin) + { + _currentMin = Input.Value; _timeSinceNewMin = 0; } - protected override void ManageState(bool isNew) - { - if (isNew) - { - _p_currentMin = _currentMin; - _lastValidValue = Input.Value; - _index++; - _timeSinceNewMin++; - _p_timeSinceNewMin = _timeSinceNewMin; - } - else - { - _currentMin = _p_currentMin; - _timeSinceNewMin = _p_timeSinceNewMin; - } - } + double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period); + _currentMin += decayRate * (_buffer.Average() - _currentMin); + _currentMin = Math.Max(_currentMin, _buffer.Min()); - protected override double Calculation() - { - ManageState(Input.IsNew); - _buffer.Add(Input.Value, Input.IsNew); - - if (Input.Value <= _currentMin) - { - _currentMin = Input.Value; - _timeSinceNewMin = 0; - } - - double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period); - _currentMin = _currentMin + decayRate * (_buffer.Average() - _currentMin); - _currentMin = Math.Max(_currentMin, _buffer.Min()); - - IsHot = true; - return _currentMin; - } + IsHot = true; + return _currentMin; } -} \ No newline at end of file +} diff --git a/lib/statistics/Mode.cs b/lib/statistics/Mode.cs index 3aacd879..a12a1fd6 100644 --- a/lib/statistics/Mode.cs +++ b/lib/statistics/Mode.cs @@ -1,11 +1,35 @@ namespace QuanTAlib; +/// +/// Represents a mode calculator that determines the most frequent value in a specified period. +/// If multiple values have the same highest frequency, it returns their average. +/// +/// +/// The Mode class uses a circular buffer to store values and calculates the mode +/// efficiently. Before the specified period is reached, it returns the average of +/// the available values as an approximation. +/// +/// In financial analysis, the mode can be useful for: +/// - Identifying the most common price levels, which could indicate support or resistance. +/// - Analyzing the distribution of returns or other financial metrics. +/// - Detecting patterns in trading volume or other discrete financial data. +/// public class Mode : AbstractBase { - public readonly int Period; - private CircularBuffer _buffer; + /// + /// The number of data points to consider for the mode calculation. + /// + private readonly int Period; + private readonly CircularBuffer _buffer; - public Mode(int period) : base() + /// + /// Initializes a new instance of the Mode class with the specified period. + /// + /// The period over which to calculate the mode. + /// + /// Thrown when period is less than 1. + /// + public Mode(int period) { if (period < 1) { @@ -18,17 +42,30 @@ public class Mode : AbstractBase Init(); } + /// + /// Initializes a new instance of the Mode class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the mode. public Mode(object source, int period) : this(period) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Resets the Mode indicator to its initial state. + /// public override void Init() { base.Init(); + _buffer.Clear(); } + /// + /// Manages the state of the Mode instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. protected override void ManageState(bool isNew) { if (isNew) @@ -38,6 +75,18 @@ public class Mode : AbstractBase } } + /// + /// Performs the mode calculation for the current period. + /// + /// + /// The calculated mode (most frequent value) for the current period. + /// If multiple values have the same highest frequency, returns their average. + /// + /// + /// Before the specified period is reached, this method returns the average of + /// the available values as an approximation of the mode. Once the period is + /// reached, it calculates the true mode by grouping and counting the values. + /// protected override double Calculation() { ManageState(Input.IsNew); diff --git a/lib/statistics/Percentile.cs b/lib/statistics/Percentile.cs index d90afe0e..ad1bdba6 100644 --- a/lib/statistics/Percentile.cs +++ b/lib/statistics/Percentile.cs @@ -1,15 +1,44 @@ namespace QuanTAlib; -using System; -using System.Linq; - +/// +/// Represents a percentile calculator that determines the value at a specified percentile +/// in a given period of data points. +/// +/// +/// The Percentile class uses a circular buffer to store values and calculates the +/// percentile efficiently. It uses linear interpolation when the percentile falls +/// between two data points. Before the specified period is reached, it returns the +/// average of the available values as an approximation. +/// +/// In financial analysis, percentiles are useful for: +/// - Assessing the relative standing of a value within a distribution. +/// - Identifying outliers or extreme values in financial data. +/// - Creating risk measures, such as Value at Risk (VaR) calculations. +/// - Analyzing the distribution of returns, trading volumes, or other financial metrics. +/// public class Percentile : AbstractBase { - public readonly int Period; - public readonly double Percent; - private CircularBuffer _buffer; + /// + /// The number of data points to consider for the percentile calculation. + /// + private readonly int Period; - public Percentile(int period, double percent) : base() + /// + /// The percentile to calculate (between 0 and 100). + /// + private readonly double Percent; + + private readonly CircularBuffer _buffer; + + /// + /// Initializes a new instance of the Percentile class with the specified period and percentile. + /// + /// The period over which to calculate the percentile. + /// The percentile to calculate (between 0 and 100). + /// + /// Thrown when period is less than 2 or percent is not between 0 and 100. + /// + public Percentile(int period, double percent) { if (period < 2) { @@ -21,24 +50,37 @@ public class Percentile : AbstractBase } Period = period; Percent = percent; - WarmupPeriod = 2; + WarmupPeriod = 2; // Minimum number of points needed for percentile calculation _buffer = new CircularBuffer(period); Name = $"Percentile(period={period}, percent={percent})"; Init(); } + /// + /// Initializes a new instance of the Percentile class with the specified source, period, and percentile. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the percentile. + /// The percentile to calculate (between 0 and 100). public Percentile(object source, int period, double percent) : this(period, percent) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Initializes the Percentile instance by clearing the buffer. + /// public override void Init() { base.Init(); _buffer.Clear(); } + /// + /// Manages the state of the Percentile instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. protected override void ManageState(bool isNew) { if (isNew) @@ -48,7 +90,19 @@ public class Percentile : AbstractBase } } -protected override double Calculation() + /// + /// Performs the percentile calculation for the current period. + /// + /// + /// The calculated percentile value for the current period. + /// + /// + /// This method uses linear interpolation when the percentile falls between two data points. + /// Before the specified period is reached, it returns the average of the available values + /// as an approximation. Once the period is reached, it calculates the true percentile by + /// sorting the values and interpolating as necessary. + /// + protected override double Calculation() { ManageState(Input.IsNew); _buffer.Add(Input.Value, Input.IsNew); @@ -85,4 +139,4 @@ protected override double Calculation() IsHot = _buffer.Count >= Period; return result; } -} \ No newline at end of file +} diff --git a/lib/statistics/Skew.cs b/lib/statistics/Skew.cs index f7aaeec8..75711882 100644 --- a/lib/statistics/Skew.cs +++ b/lib/statistics/Skew.cs @@ -1,14 +1,40 @@ namespace QuanTAlib; -using System; -using System.Linq; - +/// +/// Represents a skewness calculator that measures the asymmetry of the probability +/// distribution of a real-valued random variable about its mean. +/// +/// +/// The Skew class uses a circular buffer to store values and calculates the skewness +/// efficiently. It uses the adjusted Fisher-Pearson standardized moment coefficient +/// for sample skewness calculation. A minimum of 3 data points is required for the +/// calculation. +/// +/// In financial analysis, skewness is important for: +/// - Assessing the asymmetry of returns distribution. +/// - Evaluating the risk of extreme events in either direction. +/// - Complementing other risk measures like standard deviation. +/// - Informing investment decisions and risk management strategies. +/// +/// Positive skewness indicates a longer tail on the right side of the distribution, +/// while negative skewness indicates a longer tail on the left side. +/// public class Skew : AbstractBase { - public readonly int Period; - private CircularBuffer _buffer; + /// + /// The number of data points to consider for the skewness calculation. + /// + private readonly int Period; + private readonly CircularBuffer _buffer; - public Skew(int period) : base() + /// + /// Initializes a new instance of the Skew class with the specified period. + /// + /// The period over which to calculate the skewness. + /// + /// Thrown when period is less than 3. + /// + public Skew(int period) { if (period < 3) { @@ -21,18 +47,30 @@ public class Skew : AbstractBase Init(); } + /// + /// Initializes a new instance of the Skew class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the skewness. public Skew(object source, int period) : this(period) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Initializes the Skew instance by clearing the buffer. + /// public override void Init() { base.Init(); _buffer.Clear(); } + /// + /// Manages the state of the Skew instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. protected override void ManageState(bool isNew) { if (isNew) @@ -42,6 +80,23 @@ public class Skew : AbstractBase } } + /// + /// Performs the skewness calculation for the current period. + /// + /// + /// The calculated skewness value for the current period. + /// + /// + /// This method uses the adjusted Fisher-Pearson standardized moment coefficient + /// to calculate the sample skewness. It requires at least 3 data points for the + /// calculation. If there are fewer than 3 data points, or if the standard + /// deviation is zero, the method returns 0. + /// + /// Interpretation of results: + /// - Positive values indicate right-skewed distribution (longer tail on the right side). + /// - Negative values indicate left-skewed distribution (longer tail on the left side). + /// - Values close to 0 suggest a relatively symmetric distribution. + /// protected override double Calculation() { ManageState(Input.IsNew); @@ -49,8 +104,8 @@ public class Skew : AbstractBase _buffer.Add(Input.Value, Input.IsNew); double skew = 0; - if (_buffer.Count >= 3) // We need at least 3 data points for skewness - { + if (_buffer.Count >= 3) + { // We need at least 3 data points for skewness var values = _buffer.GetSpan().ToArray(); double mean = values.Average(); double n = values.Length; @@ -70,8 +125,8 @@ public class Skew : AbstractBase double m2 = sumSquaredDeviations / n; double s3 = Math.Pow(m2, 1.5); - if (s3 != 0) // Avoid division by zero - { + if (s3 != 0) + { // Avoid division by zero skew = (Math.Sqrt(n * (n - 1)) / (n - 2)) * (m3 / s3); } } @@ -79,4 +134,4 @@ public class Skew : AbstractBase IsHot = _buffer.Count >= Period; return skew; } -} \ No newline at end of file +} diff --git a/lib/statistics/Slope.cs b/lib/statistics/Slope.cs new file mode 100644 index 00000000..e8681069 --- /dev/null +++ b/lib/statistics/Slope.cs @@ -0,0 +1,191 @@ +namespace QuanTAlib; + +/// +/// Represents a slope calculator that performs linear regression on a series of data points. +/// +/// +/// The Slope class calculates the slope of a linear regression line, along with other +/// statistical measures such as intercept, standard deviation, R-squared, and the last +/// point on the regression line. It uses the least squares method for calculation. +/// +/// In financial analysis, slope is important for: +/// - Identifying trends in price movements or other financial metrics. +/// - Measuring the rate of change in a financial time series. +/// - Assessing the strength and direction of relationships between variables. +/// - Supporting technical analysis indicators and trading strategies. +/// +public class Slope : AbstractBase +{ + private readonly int _period; + private readonly CircularBuffer _buffer; + private readonly CircularBuffer _timeBuffer; + + /// + /// Gets the y-intercept of the regression line. + /// + public double? Intercept { get; private set; } + + /// + /// Gets the standard deviation of the y-values. + /// + public double? StdDev { get; private set; } + + /// + /// Gets the R-squared value, indicating the goodness of fit of the regression line. + /// + public double? RSquared { get; private set; } + + /// + /// Gets the y-value of the last point on the regression line. + /// + public double? Line { get; private set; } + + /// + /// Initializes a new instance of the Slope class with the specified period. + /// + /// The period over which to calculate the slope. + /// + /// Thrown when period is less than or equal to 1. + /// + public Slope(int period) + { + if (period <= 1) + { + throw new ArgumentOutOfRangeException(nameof(period), period, + "Period must be greater than 1 for Slope/Linear Regression."); + } + _period = period; + WarmupPeriod = period; + _buffer = new CircularBuffer(period); + _timeBuffer = new CircularBuffer(period); + Name = $"Slope(period={period})"; + + Init(); + } + + /// + /// Initializes a new instance of the Slope class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the slope. + public Slope(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Slope instance by clearing buffers and resetting calculated values. + /// + public override void Init() + { + base.Init(); + _buffer.Clear(); + _timeBuffer.Clear(); + Intercept = null; + StdDev = null; + RSquared = null; + Line = null; + } + + /// + /// Manages the state of the Slope instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the slope calculation using linear regression for the current period. + /// + /// + /// The calculated slope value for the current period. + /// + /// + /// This method uses the least squares method to calculate the slope of the regression line. + /// It also calculates and updates the Intercept, StdDev, RSquared, and Line properties. + /// If there are fewer than 2 data points, or if the sum of squared x deviations is 0, + /// the method returns 0 and sets the additional properties to null. + /// + /// Interpretation of results: + /// - Positive slope: Indicates an upward trend in the data. + /// - Negative slope: Indicates a downward trend in the data. + /// - Slope close to 0: Indicates a relatively flat or no clear trend in the data. + /// The magnitude of the slope represents the rate of change in the dependent variable + /// (y) for each unit change in the independent variable (x). + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + _timeBuffer.Add(Input.Time.Ticks, Input.IsNew); + + double slope = 0; + + if (_buffer.Count < 2) + { + return slope; // Return 0 when there are fewer than 2 points + } + + int count = Math.Min(_buffer.Count, _period); + var values = _buffer.GetSpan().ToArray(); + + // Calculate averages + double sumX = 0, sumY = 0; + for (int i = 0; i < count; i++) + { + sumX += i + 1; + sumY += values[i]; + } + double avgX = sumX / count; + double avgY = sumY / count; + + // Least squares method + double sumSqX = 0, sumSqY = 0, sumSqXY = 0; + for (int i = 0; i < count; i++) + { + double devX = (i + 1) - avgX; + double devY = values[i] - avgY; + sumSqX += devX * devX; + sumSqY += devY * devY; + sumSqXY += devX * devY; + } + + if (sumSqX > 0) + { + slope = sumSqXY / sumSqX; + Intercept = avgY - (slope * avgX); + + // Calculate Standard Deviation and R-Squared + double stdDevX = Math.Sqrt(sumSqX / count); + double stdDevY = Math.Sqrt(sumSqY / count); + StdDev = stdDevY; + + if (stdDevX * stdDevY != 0) + { + double r = sumSqXY / (stdDevX * stdDevY) / count; + RSquared = r * r; + } + + // Calculate last Line value (y = mx + b) + Line = (slope * count) + Intercept; + } + else + { + Intercept = null; + StdDev = null; + RSquared = null; + Line = null; + } + + IsHot = _buffer.Count == _period; + return slope; + } +} diff --git a/lib/statistics/Stddev.cs b/lib/statistics/Stddev.cs index d1eff641..aae6fb20 100644 --- a/lib/statistics/Stddev.cs +++ b/lib/statistics/Stddev.cs @@ -1,69 +1,130 @@ -using System; -using System.Linq; +namespace QuanTAlib; -namespace QuanTAlib +/// +/// Represents a standard deviation calculator that measures the amount of variation or +/// dispersion of a set of values. +/// +/// +/// The Stddev class calculates either the population standard deviation or the sample +/// standard deviation based on the isPopulation parameter. It uses a circular buffer +/// to efficiently manage the data points within the specified period. +/// +/// In financial analysis, standard deviation is important for: +/// - Measuring volatility of financial instruments or portfolios. +/// - Assessing risk in investments. +/// - Calculating Sharpe ratios and other risk-adjusted performance measures. +/// - Identifying potential outliers or unusual market behavior. +/// +public class Stddev : AbstractBase { - public class Stddev : AbstractBase + /// + /// Indicates whether to calculate population (true) or sample (false) standard deviation. + /// + private readonly bool IsPopulation; + + /// + /// Circular buffer to store the most recent data points. + /// + private readonly CircularBuffer _buffer; + + /// + /// Initializes a new instance of the Stddev class with the specified period and + /// population flag. + /// + /// The period over which to calculate the standard deviation. + /// + /// A flag indicating whether to calculate population (true) or sample (false) standard deviation. + /// + /// + /// Thrown when period is less than 2. + /// + public Stddev(int period, bool isPopulation = false) { - public readonly int Period; - public readonly bool IsPopulation; - private CircularBuffer _buffer; - - public Stddev(int period, bool isPopulation = false) : base() + if (period < 2) { - if (period < 2) - { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); - } - Period = period; - IsPopulation = isPopulation; - WarmupPeriod = 0; - _buffer = new CircularBuffer(period); - Name = $"Stddev(period={period}, population={isPopulation})"; - Init(); + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); } + IsPopulation = isPopulation; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + Name = $"Stddev(period={period}, population={isPopulation})"; + Init(); + } - public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation) + /// + /// Initializes a new instance of the Stddev class with the specified source, period, + /// and population flag. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the standard deviation. + /// + /// A flag indicating whether to calculate population (true) or sample (false) standard deviation. + /// + public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Stddev instance by clearing the buffer. + /// + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + /// + /// Manages the state of the Stddev instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + 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(); - _buffer.Clear(); - } - - protected override void ManageState(bool isNew) - { - if (isNew) - { - _lastValidValue = Input.Value; - _index++; - } - } - - protected override double Calculation() - { - ManageState(Input.IsNew); - - _buffer.Add(Input.Value, Input.IsNew); - - double stddev = 0; - if (_buffer.Count > 1) - { - var values = _buffer.GetSpan().ToArray(); - double mean = values.Average(); - double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2)); - - double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1; - double variance = sumOfSquaredDifferences / divisor; - stddev = Math.Sqrt(variance); - } - - IsHot = true; // StdDev calc is valid from bar 1 - return stddev; + _lastValidValue = Input.Value; + _index++; } } -} \ No newline at end of file + + /// + /// Performs the standard deviation calculation for the current period. + /// + /// + /// The calculated standard deviation value for the current period. + /// + /// + /// This method calculates the standard deviation using the formula: + /// sqrt(sum((x - mean)^2) / n) for population, or + /// sqrt(sum((x - mean)^2) / (n - 1)) for sample, + /// where x is each value, mean is the average of all values, and n is the number of values. + /// If there's only one value in the buffer, the method returns 0. + /// + /// Interpretation of results: + /// - A low standard deviation indicates that the values tend to be close to the mean. + /// - A high standard deviation indicates that the values are spread out over a wider range. + /// - In financial contexts, higher standard deviation often implies higher volatility or risk. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double stddev = 0; + if (_buffer.Count > 1) + { + var values = _buffer.GetSpan().ToArray(); + double mean = values.Average(); + double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2)); + + double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1; + double variance = sumOfSquaredDifferences / divisor; + stddev = Math.Sqrt(variance); + } + + IsHot = true; // StdDev calc is valid from bar 1 + return stddev; + } +} diff --git a/lib/statistics/Variance.cs b/lib/statistics/Variance.cs index 675958ca..e1bc4c67 100644 --- a/lib/statistics/Variance.cs +++ b/lib/statistics/Variance.cs @@ -1,68 +1,130 @@ -using System; -using System.Linq; +namespace QuanTAlib; -namespace QuanTAlib +/// +/// Represents a variance calculator that measures the spread of a set of numbers +/// from their average value. +/// +/// +/// The Variance class calculates either the population variance or the sample +/// variance based on the isPopulation parameter. It uses a circular buffer +/// to efficiently manage the data points within the specified period. +/// +/// In financial analysis, variance is important for: +/// - Measuring the dispersion of returns around the mean. +/// - Assessing risk and volatility in financial instruments or portfolios. +/// - Serving as a basis for other risk measures like standard deviation and beta. +/// - Contributing to portfolio optimization techniques, such as Modern Portfolio Theory. +/// +public class Variance : AbstractBase { - public class Variance : AbstractBase + /// + /// Indicates whether to calculate population (true) or sample (false) variance. + /// + private readonly bool IsPopulation; + + /// + /// Circular buffer to store the most recent data points. + /// + private readonly CircularBuffer _buffer; + + /// + /// Initializes a new instance of the Variance class with the specified period and + /// population flag. + /// + /// The period over which to calculate the variance. + /// + /// A flag indicating whether to calculate population (true) or sample (false) variance. + /// + /// + /// Thrown when period is less than 2. + /// + public Variance(int period, bool isPopulation = false) { - public readonly int Period; - public readonly bool IsPopulation; - private CircularBuffer _buffer; - - public Variance(int period, bool isPopulation = false) : base() + if (period < 2) { - if (period < 2) - { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); - } - Period = period; - IsPopulation = isPopulation; - WarmupPeriod = 0; - _buffer = new CircularBuffer(period); - Name = $"Variance(period={period}, population={isPopulation})"; - Init(); + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); } + IsPopulation = isPopulation; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + Name = $"Variance(period={period}, population={isPopulation})"; + Init(); + } - public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation) + /// + /// Initializes a new instance of the Variance class with the specified source, period, + /// and population flag. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the variance. + /// + /// A flag indicating whether to calculate population (true) or sample (false) variance. + /// + public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Variance instance by clearing the buffer. + /// + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + /// + /// Manages the state of the Variance instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + 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(); - _buffer.Clear(); - } - - protected override void ManageState(bool isNew) - { - if (isNew) - { - _lastValidValue = Input.Value; - _index++; - } - } - - protected override double Calculation() - { - ManageState(Input.IsNew); - - _buffer.Add(Input.Value, Input.IsNew); - - double variance = 0; - if (_buffer.Count > 1) - { - var values = _buffer.GetSpan().ToArray(); - double mean = values.Average(); - double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2)); - - double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1; - variance = sumOfSquaredDifferences / divisor; - } - - IsHot = true; - return variance; + _lastValidValue = Input.Value; + _index++; } } -} \ No newline at end of file + + /// + /// Performs the variance calculation for the current period. + /// + /// + /// The calculated variance value for the current period. + /// + /// + /// This method calculates the variance using the formula: + /// sum((x - mean)^2) / n for population, or + /// sum((x - mean)^2) / (n - 1) for sample, + /// where x is each value, mean is the average of all values, and n is the number of values. + /// If there's only one value in the buffer, the method returns 0. + /// + /// Interpretation of results: + /// - A low variance indicates that the values tend to be close to the mean and to each other. + /// - A high variance indicates that the values are spread out over a wider range. + /// - In financial contexts, higher variance often implies higher volatility or risk. + /// - Variance is always non-negative, and its units are squared units of the original data. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double variance = 0; + if (_buffer.Count > 1) + { + var values = _buffer.GetSpan().ToArray(); + double mean = values.Average(); + double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2)); + + double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1; + variance = sumOfSquaredDifferences / divisor; + } + + IsHot = true; + return variance; + } +} diff --git a/lib/statistics/Zscore.cs b/lib/statistics/Zscore.cs index 87f59b67..de94054c 100644 --- a/lib/statistics/Zscore.cs +++ b/lib/statistics/Zscore.cs @@ -1,14 +1,40 @@ namespace QuanTAlib; -using System; -using System.Linq; - +/// +/// Represents a Z-score calculator that measures how many standard deviations +/// an element is from the mean of a set of values. +/// +/// +/// The Zscore class calculates the Z-score (also known as standard score) for +/// the most recent value in a given period. It uses a circular buffer to +/// efficiently manage the data points within the specified period. +/// +/// In financial analysis, Z-score is important for: +/// - Identifying outliers or unusual price movements. +/// - Normalizing data across different scales or time periods. +/// - Assessing the relative position of a value within its historical distribution. +/// - Supporting trading strategies based on mean reversion or momentum. +/// public class Zscore : AbstractBase { - public readonly int Period; - private CircularBuffer _buffer; + /// + /// The number of data points to consider for the Z-score calculation. + /// + private readonly int Period; - public Zscore(int period) : base() + /// + /// Circular buffer to store the most recent data points. + /// + private readonly CircularBuffer _buffer; + + /// + /// Initializes a new instance of the Zscore class with the specified period. + /// + /// The period over which to calculate the Z-score. + /// + /// Thrown when period is less than 2. + /// + public Zscore(int period) { if (period < 2) { @@ -21,18 +47,30 @@ public class Zscore : AbstractBase Init(); } + /// + /// Initializes a new instance of the Zscore class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Z-score. public Zscore(object source, int period) : this(period) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Initializes the Zscore instance by clearing the buffer. + /// public override void Init() { base.Init(); _buffer.Clear(); } + /// + /// Manages the state of the Zscore instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. protected override void ManageState(bool isNew) { if (isNew) @@ -42,6 +80,26 @@ public class Zscore : AbstractBase } } + /// + /// Performs the Z-score calculation for the current period. + /// + /// + /// The calculated Z-score value for the most recent input in the current period. + /// + /// + /// This method calculates the Z-score using the formula: + /// Z = (x - μ) / σ + /// where x is the input value, μ is the mean of the period, and σ is the sample standard deviation. + /// If there are fewer than 2 data points or if the standard deviation is 0, the method returns 0. + /// + /// Interpretation of results: + /// - A Z-score of 0 indicates that the data point is exactly on the mean. + /// - A positive Z-score indicates the data point is above the mean. + /// - A negative Z-score indicates the data point is below the mean. + /// - The magnitude of the Z-score represents how many standard deviations away from the mean the data point is. + /// - In a normal distribution, about 68% of the values have a Z-score between -1 and 1, + /// 95% between -2 and 2, and 99.7% between -3 and 3. + /// protected override double Calculation() { ManageState(Input.IsNew); @@ -49,8 +107,8 @@ public class Zscore : AbstractBase _buffer.Add(Input.Value, Input.IsNew); double zScore = 0; - if (_buffer.Count >= 2) // We need at least 2 data points for Z-score - { + if (_buffer.Count >= 2) + { // We need at least 2 data points for Z-score var values = _buffer.GetSpan().ToArray(); double mean = values.Average(); double n = values.Length; @@ -58,8 +116,8 @@ public class Zscore : AbstractBase double sumSquaredDeviations = values.Sum(x => Math.Pow(x - mean, 2)); double standardDeviation = Math.Sqrt(sumSquaredDeviations / (n - 1)); // Sample standard deviation - if (standardDeviation != 0) // Avoid division by zero - { + if (standardDeviation != 0) + { // Avoid division by zero zScore = (Input.Value - mean) / standardDeviation; } } @@ -67,4 +125,4 @@ public class Zscore : AbstractBase IsHot = _buffer.Count >= Period; return zScore; } -} \ No newline at end of file +} diff --git a/lib/volatility/Atr.cs b/lib/volatility/Atr.cs new file mode 100644 index 00000000..3ea3e5f0 --- /dev/null +++ b/lib/volatility/Atr.cs @@ -0,0 +1,110 @@ +namespace QuanTAlib; + +/// +/// Represents an Average True Range (ATR) calculator, a measure of market volatility. +/// +/// +/// The ATR class calculates the average true range using a Relative Moving Average (RMA) +/// 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. +/// +public class Atr : AbstractBase +{ + public double Tr { get; private set; } + private readonly Rma _ma; + private double _prevClose, _p_prevClose; + + /// + /// Initializes a new instance of the Atr class with the specified period. + /// + /// The period over which to calculate the ATR. + /// + /// Thrown when period is less than 1. + /// + public Atr(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _ma = new(period, useSma: true); + WarmupPeriod = _ma.WarmupPeriod; + Name = $"ATR({period})"; + } + + /// + /// Initializes a new instance of the Atr class with the specified source and period. + /// + /// The source object to subscribe to for bar updates. + /// The period over which to calculate the ATR. + public Atr(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new BarSignal(Sub)); + } + + /// + /// Initializes the Atr instance by setting up the initial state. + /// + public override void Init() + { + base.Init(); + _ma.Init(); + _prevClose = double.NaN; + Tr = 0; + } + + /// + /// Manages the state of the Atr instance based on whether a new bar is being processed. + /// + /// Indicates whether the current input is a new bar. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + _p_prevClose = _prevClose; + } + else + { + _prevClose = _p_prevClose; + } + } + + /// + /// Performs the ATR calculation for the current bar. + /// + /// + /// The calculated ATR value for the current bar. + /// + /// + /// This method calculates the true range for the current bar and then uses an RMA + /// to smooth the true range values. For the first bar, it uses the high-low range + /// as the true range. + /// + protected override double Calculation() + { + ManageState(BarInput.IsNew); + + if (_index == 1) + { + Tr = BarInput.High - BarInput.Low; + _prevClose = BarInput.Close; + } + else + { + Tr = Math.Max( + BarInput.High - BarInput.Low, + Math.Max( + Math.Abs(BarInput.High - _prevClose), + Math.Abs(BarInput.Low - _prevClose) + ) + ); + } + _ma.Calc(new TValue(Input.Time, Tr, BarInput.IsNew)); + + IsHot = _ma.IsHot; + _prevClose = BarInput.Close; + return _ma.Value; + } +} diff --git a/lib/volatility/Historical.cs b/lib/volatility/Historical.cs new file mode 100644 index 00000000..9417b14d --- /dev/null +++ b/lib/volatility/Historical.cs @@ -0,0 +1,128 @@ +namespace QuanTAlib; + +/// +/// Represents a historical volatility calculator that measures the dispersion of returns +/// for a given security or market index over a specific period. +/// +/// +/// The Historical class calculates volatility based on logarithmic returns. It can provide +/// both annualized and non-annualized volatility measures. The calculation uses a sample +/// standard deviation formula and assumes 252 trading days in a year for annualization. +/// +public class Historical : AbstractBase +{ + private readonly int Period; + private readonly bool IsAnnualized; + private readonly CircularBuffer _buffer; + private readonly CircularBuffer _logReturns; + private double _previousClose; + + /// + /// Initializes a new instance of the Historical class with the specified period and annualization flag. + /// + /// The period over which to calculate historical volatility. + /// Whether to annualize the volatility (default is true). + /// + /// Thrown when period is less than 2. + /// + public Historical(int period, bool isAnnualized = true) + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + Period = period; + IsAnnualized = isAnnualized; + WarmupPeriod = period + 1; // We need one extra data point to calculate the first return + _buffer = new CircularBuffer(period + 1); + _logReturns = new CircularBuffer(period); + Name = $"Historical(period={period}, annualized={isAnnualized})"; + Init(); + } + + /// + /// Initializes a new instance of the Historical class with the specified source, period, and annualization flag. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate historical volatility. + /// Whether to annualize the volatility (default is true). + public Historical(object source, int period, bool isAnnualized = true) : this(period, isAnnualized) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Historical instance by clearing buffers and resetting the previous close value. + /// + public override void Init() + { + base.Init(); + _buffer.Clear(); + _logReturns.Clear(); + _previousClose = 0; + } + + /// + /// Manages the state of the Historical instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the historical volatility calculation for the current period. + /// + /// + /// The calculated historical volatility value for the current period. + /// + /// + /// This method calculates the volatility using the following steps: + /// 1. Compute logarithmic returns. + /// 2. Calculate the sample standard deviation of the log returns. + /// 3. If annualized, multiply by the square root of 252 (assumed trading days in a year). + /// The method returns 0 until enough data points are available for the calculation. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double volatility = 0; + if (_buffer.Count > 1) + { + if (_previousClose != 0) + { + double logReturn = Math.Log(Input.Value / _previousClose); + _logReturns.Add(logReturn, Input.IsNew); + } + + if (_logReturns.Count == Period) + { + var returns = _logReturns.GetSpan().ToArray(); + double mean = returns.Average(); + double sumOfSquaredDifferences = returns.Sum(x => Math.Pow(x - mean, 2)); + + double variance = sumOfSquaredDifferences / (Period - 1); // Using sample standard deviation + volatility = Math.Sqrt(variance); + + if (IsAnnualized) + { + // Assuming 252 trading days in a year. Adjust as needed. + volatility *= Math.Sqrt(252); + } + } + } + + _previousClose = Input.Value; + IsHot = _index >= WarmupPeriod; + return volatility; + } +} diff --git a/lib/volatility/Jvolty.cs b/lib/volatility/Jvolty.cs new file mode 100644 index 00000000..23f6ae97 --- /dev/null +++ b/lib/volatility/Jvolty.cs @@ -0,0 +1,173 @@ +/// +/// Represents a Jurik Volatility (Jvolty) calculator, a measure of market volatility based on Jurik Moving Average (JMA) concepts. +/// + +namespace QuanTAlib; + +public class Jvolty : AbstractBase +{ + private readonly int _period; + private readonly double _phase; + private readonly CircularBuffer _vsumBuff; + private readonly CircularBuffer _avoltyBuff; + + private double _len1; + private double _pow1; + private readonly double _beta; + private double _upperBand, _lowerBand, _p_upperBand, _p_lowerBand; + private double _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma; + private double _vSum, _p_vSum; + + + public double UpperBand { get; set; } + public double LowerBand { get; set; } + public double Volty { get; set; } + public double VSum { get; set; } + public double Jma { get; set; } + public double AvgVolty { get; set; } + + + /// + /// Initializes a new instance of the Jvolty class with the specified parameters. + /// + /// The period over which to calculate the Jvolty. + /// The phase parameter for the JMA-style calculation. + /// The short-term volatility period. + /// + /// Thrown when period is less than 1. + /// + public Jvolty(int period, int phase = 0) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _period = period; + _phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5); + + _vsumBuff = new CircularBuffer(10); + _avoltyBuff = new CircularBuffer(65); + _beta = 0.45 * (period - 1) / (0.45 * (period - 1) + 2); + + WarmupPeriod = period * 2; + Name = $"JVOLTY({period})"; + } + + /// + /// Initializes a new instance of the Jvolty class with the specified source and parameters. + /// + /// The source object to subscribe to for bar updates. + /// The period over which to calculate the Jvolty. + /// The phase parameter for the JMA-style calculation. + /// The short-term volatility period. + public Jvolty(object source, int period, int phase = 0) : this(period, phase) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new BarSignal(Sub)); + } + + /// + /// Initializes the Jvolty instance by setting up the initial state. + /// + public override void Init() + { + base.Init(); + _upperBand = _lowerBand = 0.0; + _p_upperBand = _p_lowerBand = 0.0; + _len1 = Math.Max((Math.Log(Math.Sqrt(_period - 1)) / Math.Log(2.0)) + 2.0, 0); + _pow1 = Math.Max(_len1 - 2.0, 0.5); + _avoltyBuff.Clear(); + _vsumBuff.Clear(); + } + + /// + /// Manages the state of the Jvolty instance based on whether a new bar is being processed. + /// + /// Indicates whether the current input is a new bar. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + _p_upperBand = _upperBand; + _p_lowerBand = _lowerBand; + _p_vSum = _vSum; + _p_prevMa1 = _prevMa1; + _p_prevDet0 = _prevDet0; + _p_prevDet1 = _prevDet1; + _p_prevJma = _prevJma; + } + else + { + _upperBand = _p_upperBand; + _lowerBand = _p_lowerBand; + _vSum = _p_vSum; + _prevMa1 = _p_prevMa1; + _prevDet0 = _p_prevDet0; + _prevDet1 = _p_prevDet1; + _prevJma = _p_prevJma; + } + } + + /// + /// Performs the Jvolty calculation for the current bar. + /// + /// + /// The calculated Jvolty value for the current bar. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double price = Input.Value; + if (_index == 1) + { + _upperBand = _lowerBand = price; + } + + double del1 = price - _upperBand; + double del2 = price - _lowerBand; + double volty = Math.Max(Math.Abs(del1), Math.Abs(del2)); + + _vsumBuff.Add(volty, Input.IsNew); + _vSum += (_vsumBuff[^1] - _vsumBuff[0]) / 10; + _avoltyBuff.Add(_vSum, Input.IsNew); + double avgvolty = _avoltyBuff.Average(); + + double rvolty = (avgvolty > 0) ? volty / avgvolty : 1; + rvolty = Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1)); + + double pow2 = Math.Pow(rvolty, _pow1); + double Kv = Math.Pow(_beta, Math.Sqrt(pow2)); + + _upperBand = (del1 >= 0) ? price : price - (Kv * del1); + _lowerBand = (del2 <= 0) ? price : price - (Kv * del2); + + + + double alpha = Math.Pow(_beta, pow2); + double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1; + _prevMa1 = ma1; + + double det0 = (price - ma1) * (1 - _beta) + _beta * _prevDet0; + _prevDet0 = det0; + double ma2 = ma1 + _phase * det0; + + double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha) ) + (alpha * alpha * _prevDet1); + _prevDet1 = det1; + double jma = _prevJma + det1; + _prevJma = jma; + + UpperBand = _upperBand; + LowerBand = _lowerBand; + Volty = volty; + VSum = _vSum; + AvgVolty = avgvolty; + Jma = jma; + + IsHot = _index >= WarmupPeriod; + return volty; + } +} + + diff --git a/lib/volatility/Realized.cs b/lib/volatility/Realized.cs new file mode 100644 index 00000000..5798919b --- /dev/null +++ b/lib/volatility/Realized.cs @@ -0,0 +1,116 @@ +namespace QuanTAlib; + +/// +/// Represents a realized volatility calculator that measures the actual price fluctuations +/// observed in the market over a specific period. +/// +/// +/// The Realized class calculates volatility based on logarithmic returns. It can provide +/// both annualized and non-annualized volatility measures. The calculation uses a rolling +/// sum of squared returns for efficiency and assumes 252 trading days in a year for annualization. +/// +public class Realized : AbstractBase +{ + private readonly int Period; + private readonly bool IsAnnualized; + private readonly CircularBuffer _returns; + private double _previousClose; + private double _sumSquaredReturns; + + /// + /// Initializes a new instance of the Realized class with the specified period and annualization flag. + /// + /// The period over which to calculate realized volatility. + /// Whether to annualize the volatility (default is true). + /// + /// Thrown when period is less than 2. + /// + public Realized(int period, bool isAnnualized = true) + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + Period = period; + IsAnnualized = isAnnualized; + WarmupPeriod = period + 1; // We need one extra data point to calculate the first return + _returns = new CircularBuffer(period); + Name = $"Realized(period={period}, annualized={isAnnualized})"; + Init(); + } + + /// + /// Initializes the Realized instance by clearing buffers and resetting calculation variables. + /// + public override void Init() + { + base.Init(); + _returns.Clear(); + _previousClose = 0; + _sumSquaredReturns = 0; + } + + /// + /// Manages the state of the Realized instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the realized volatility calculation for the current period. + /// + /// + /// The calculated realized volatility value for the current period. + /// + /// + /// This method calculates the volatility using the following steps: + /// 1. Compute logarithmic returns. + /// 2. Maintain a rolling sum of squared returns. + /// 3. Calculate the variance using the sum of squared returns. + /// 4. Take the square root of the variance to get volatility. + /// 5. If annualized, multiply by the square root of 252 (assumed trading days in a year). + /// The method returns 0 until enough data points are available for the calculation. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double volatility = 0; + if (_previousClose != 0) + { + double logReturn = Math.Log(Input.Value / _previousClose); + + if (_returns.Count == Period) + { + // Remove the oldest squared return from the sum + _sumSquaredReturns -= Math.Pow(_returns[0], 2); + } + + _returns.Add(logReturn, Input.IsNew); + _sumSquaredReturns += Math.Pow(logReturn, 2); + + if (_returns.Count == Period) + { + double variance = _sumSquaredReturns / Period; + volatility = Math.Sqrt(variance); + + if (IsAnnualized) + { + // Assuming 252 trading days in a year. Adjust as needed. + volatility *= Math.Sqrt(252); + } + } + } + + _previousClose = Input.Value; + IsHot = _index >= WarmupPeriod; + return volatility; + } +} \ No newline at end of file diff --git a/lib/volatility/Rsi.cs b/lib/volatility/Rsi.cs new file mode 100644 index 00000000..9252281d --- /dev/null +++ b/lib/volatility/Rsi.cs @@ -0,0 +1,62 @@ +using System; + +namespace QuanTAlib; + +/// +/// Represents a Relative Strength Index (RSI) calculator following Wilder's algorithm. +/// +public class Rsi : AbstractBase +{ + private readonly Rma _avgGain; + private readonly Rma _avgLoss; + private double _prevValue, _p_prevValue; + + public Rsi(int period = 14) + { + if (period < 1) + throw new ArgumentOutOfRangeException(nameof(period)); + _avgGain = new(period, useSma: true); + _avgLoss = new(period, useSma: true); + _index = 0; + WarmupPeriod = period + 1; + Name = $"RSI({period})"; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + _p_prevValue = _prevValue; + } + else + { + _prevValue = _p_prevValue; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + if (_index == 1) + { + _prevValue = Input.Value; + } + + double change = Input.Value - _prevValue; + double gain = Math.Max(change, 0); + double loss = Math.Max(-change, 0); + _prevValue = Input.Value; + + _avgGain.Calc(gain, IsNew: Input.IsNew); + _avgLoss.Calc(loss, IsNew: Input.IsNew); + + double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100; + + + return rsi; + + + } +} diff --git a/lib/volatility/Rsx.cs b/lib/volatility/Rsx.cs new file mode 100644 index 00000000..6d44c26d --- /dev/null +++ b/lib/volatility/Rsx.cs @@ -0,0 +1,64 @@ +using System; + +namespace QuanTAlib; + +/// +/// Jurik's superior replacement for RSI +/// +public class Rsx : AbstractBase +{ + private readonly Rma _avgGain; + private readonly Rma _avgLoss; + private readonly Jma _rsx; + private double _prevValue, _p_prevValue; + + public Rsx(int period = 14, int phase = 0, double factor = 0.55) + { + if (period < 1) + throw new ArgumentOutOfRangeException(nameof(period)); + _avgGain = new(period); + _avgLoss = new(period); + _rsx = new(8, 100, 0.25, 3); + _index = 0; + WarmupPeriod = period + 1; + Name = $"RSX({period})"; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + _p_prevValue = _prevValue; + } + else + { + _prevValue = _p_prevValue; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + if (_index == 1) + { + _prevValue = Input.Value; + } + + double change = Input.Value - _prevValue; + double gain = Math.Max(change, 0); + double loss = Math.Max(-change, 0); + _prevValue = Input.Value; + + _avgGain.Calc(gain, IsNew: Input.IsNew); + _avgLoss.Calc(loss, IsNew: Input.IsNew); + + double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100; + double rsx = _rsx.Calc(rsi, Input.IsNew); + + return rsx; + + + } +} diff --git a/lib/volatility/Rvi.cs b/lib/volatility/Rvi.cs new file mode 100644 index 00000000..7047176c --- /dev/null +++ b/lib/volatility/Rvi.cs @@ -0,0 +1,113 @@ +namespace QuanTAlib; + +/// +/// Represents a Relative Volatility Index (RVI) calculator, which measures the direction +/// of volatility in relation to price movements. +/// +/// +/// The RVI was introduced by Donald Dorsey in the 1993 issue of Technical Analysis +/// of Stocks & Commodities Magazine. It focuses on the direction of price movements +/// in relation to volatility. The indicator uses standard deviation calculations +/// to determine whether volatility is increasing more in up moves or down moves. +/// +/// This implementation uses a combination of Standard Deviation and Simple Moving Average +/// calculations to compute the RVI. +/// +public class Rvi : AbstractBase +{ + private readonly Stddev _upStdDev, _downStdDev; + private readonly Sma _upSma, _downSma; + private double _previousClose; + + /// + /// Initializes a new instance of the Rvi class with the specified period. + /// + /// The period over which to calculate the RVI. + /// + /// Thrown when period is less than 2. + /// + public Rvi(int period) + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + int Period = period; + WarmupPeriod = period; + Name = $"RVI(period={period})"; + _upStdDev = new Stddev(Period); + _downStdDev = new Stddev(Period); + _upSma = new(Period); + _downSma = new(Period); + Init(); + } + + /// + /// Initializes a new instance of the Rvi class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the RVI. + public Rvi(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Rvi instance by setting up the initial state. + /// + public override void Init() + { + base.Init(); + _previousClose = 0; + } + + /// + /// Manages the state of the Rvi instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Value; + _index++; + } + } + + /// + /// Performs the RVI calculation for the current input. + /// + /// + /// The calculated RVI value for the current input. + /// + /// + /// This method calculates the RVI using the following steps: + /// 1. Calculate the change in price from the previous close. + /// 2. Determine the up move and down move based on the change. + /// 3. Calculate standard deviations of up and down moves. + /// 4. Apply a simple moving average to the standard deviations. + /// 5. Compute the RVI as a percentage of up volatility to total volatility. + /// The method returns 0 if the sum of up and down volatility is zero. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double close = Input.Value; + double change = close - _previousClose; + + double upMove = Math.Max(change, 0); + double downMove = Math.Max(-change, 0); + + _upSma.Calc(_upStdDev.Calc(new TValue(Input.Time, upMove, Input.IsNew))); + _downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew))); + + double rvi; + rvi = (_upSma.Value + _downSma.Value != 0) ? 100 * _upSma.Value / (_upSma.Value + _downSma.Value) : 0; + + _previousClose = close; + IsHot = _index >= WarmupPeriod; + return rvi; + } +} diff --git a/lib/volatility/todo.md b/lib/volatility/todo.md new file mode 100644 index 00000000..32636c3f --- /dev/null +++ b/lib/volatility/todo.md @@ -0,0 +1,29 @@ +# Volatility Measures + +## Single Value Input (Typically Closing Prices) + +- **Jurik Volatility (Volty)** +- **Standard Deviation** +- **RVI Relative Volatility Index** +- **CMO Chande Momentum Oscillator** +- **Historical Volatility** +- **Average True Range (ATR) (High, Low, Close)** + +- Normalized ATR +- Ulcer Index +- ARCH/GARCH Models +- Exponential Weighted Moving Average (EWMA) Volatility +- Conditional Volatility +- Volatility Ratio +- Close-to-Close Volatility +- Volatility of Volatility (VOV) +- Volatility Cone +- Bollinger Bands +- Stochastic Volatility: Typically modeled using closing prices, but can incorporate other price information +- Garman-Klass Volatility +- Rogers-Satchell Volatility +- Yang-Zhang Volatility +- Parkinson Volatility (High, Low) +- Chaikin Volatility (High, Low) +- Keltner Channels (typically Close, High, Low) +- High-Low Volatility (High, Low) diff --git a/notebooks/Skender.dib b/notebooks/Skender.dib new file mode 100644 index 00000000..52eab105 --- /dev/null +++ b/notebooks/Skender.dib @@ -0,0 +1,45 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "nuget:Skender.Stock.Indicators" +#r "..\lib\obj\Debug\QuanTAlib.dll" + +#!csharp + +using Skender.Stock.Indicators; +using QuanTAlib; + +GbmFeed gbm = new(); +Atr atr = new(gbm, 5); +TSeries res = new(atr); +gbm.Add(100); + +IEnumerable quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, + High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume }); +var SkResults = quotes.GetAtr(5).Select(i => i.Atr.Null2NaN()!); +for (int i=0; i< gbm.Length; i++) { + Console.WriteLine($"{gbm.High[i].Value,6:F2} {gbm.Low[i].Value,6:F2} {gbm.Close[i].Value,6:F2}\t\t{res[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}"); +} + +#!csharp + +Random rnd = new((int)DateTime.Now.Ticks); +GbmFeed feed = new(sigma: 0.5, mu: 0.0); +TBarSeries bars = new(feed); +feed.Add(20); +IEnumerable quotes; + +int period = rnd.Next(5) + 2; +Atr ma = new(period: period); +TSeries QL = new(); +foreach (TBar item in bars) { + Console.WriteLine($"{ma.Calc(item)}"); + //QL.Add(ma.Calc(item)); +} + +#!csharp + +bars diff --git a/notebooks/core.dib b/notebooks/core.dib index 2f997b49..1fa02006 100644 --- a/notebooks/core.dib +++ b/notebooks/core.dib @@ -4,7 +4,7 @@ #!csharp -#r "..\src\obj\Debug\QuanTAlib.dll" +#r "..\lib\obj\Debug\QuanTAlib.dll" #r "nuget:Skender.Stock.Indicators" using Skender.Stock.Indicators; @@ -13,113 +13,56 @@ QuanTAlib.Formatters.Initialize(); #!csharp +Atr ma = new(10); GbmFeed gbm = new(); -EmaCalc ema1 = new(gbm.Close, 10, useSma: false); -EmaCalc ema2 = new(gbm.Close, 10, useSma: true); -TValSeries res1 = new(ema1); -TValSeries res2 = new(ema2); -gbm.Add(50); -List mse1 = new(); -List mse2 = new(); - - +gbm.Add(30); +IEnumerable quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume }); +var SkResults = quotes.GetAtr(10).Select(i => i.Atr.Null2NaN()!); for (int i=0; i< gbm.Length; i++) { - double v= gbm.Close[i].Value; - double e1 = res1[i].Value; - mse1.Add((e1-v)*(e1-v)); - double e2 = res2[i].Value; - mse2.Add((e2-v)*(e2-v)); - - //Console.WriteLine($"{i,3} {mse1.Average(),10:F4} {mse2.Average(),10:F4}"); + ma.Calc(gbm[i]); + Console.WriteLine($"{i,3} {ma.Value,10:F3} \t {SkResults.ElementAt(i):F3}"); } - Console.WriteLine($"{mse2.Average()-mse1.Average(),10:F8}"); - -#!csharp - -display(res1); - #!csharp +Atr ma = new(10); GbmFeed gbm = new(); -EmaCalc ema1 = new(gbm.Close, 10, useSma: false); -EmaCalc ema2 = new(gbm.Close, 10, useSma: true); -TValSeries res1 = new(ema1); -TValSeries res2 = new(ema2); +gbm.Add(30); +IEnumerable quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume }); +var SkResults = quotes.GetTr().Select(i => i.Tr.Null2NaN()!); +for (int i=0; i< gbm.Length; i++) { + ma.Calc(new TBar(gbm[i])); + + Console.WriteLine($"{gbm.High[i].Value,6:F4} \t{gbm.Low[i].Value,6:F4} \t{gbm.Close[i].Value,6:F4} \t{ma.Tr,10:F4} \t{SkResults.ElementAt(i),10:F4}"); +} + +#!csharp + +//ATR test +GbmFeed gbm = new(); +TBarSeries feed = new(gbm); + +Atr ma1 = new(gbm, 10); +TSeries res1 = new(ma1); +gbm.Add(30); +IEnumerable quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume }); +var SkResults = quotes.GetAtr(10).Select(i => i.Atr.Null2NaN()!); +for (int i=0; i< gbm.Length; i++) { + double delta = Math.Round(res1[i].Value, 10) - Math.Round(SkResults.ElementAt(i), 10); + //Console.WriteLine($"{i,3} {gbm.High[i].Value,6:F2} {gbm.Low[i].Value,6:F2} {gbm.Close[i].Value,6:F2} {res1[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}\t{delta}"); + Console.WriteLine($"{i,3} h:{gbm.High[i].Value,6:F2} l:{gbm.Low[i].Value,6:F2} c:{gbm.Close[i].Value,6:F2} {res1[i].Atr,10:F4} {SkResults.ElementAt(i),10:F4}\t{delta}"); +} + +#!csharp + +//EMA test +GbmFeed gbm = new(); +Ema ema1 = new(gbm.Close, 10, useSma: true); +TSeries res1 = new(ema1); gbm.Add(30); IEnumerable quotes = gbm.Close.Select(item => new Quote { Date = item.Time, Close = (decimal)item.Value }); var SkResults = quotes.GetEma(10).Select(i => i.Ema.Null2NaN()!); for (int i=0; i< gbm.Length; i++) { - Console.WriteLine($"{i,3} {gbm.Close[i].Value,6:F2} {res1[i].Value,10:F4} {res2[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}"); + double delta = Math.Round(res1[i].Value, 10) - Math.Round(SkResults.ElementAt(i), 10); + Console.WriteLine($"{i,3} {gbm.Close[i].Value,6:F2} {res1[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}\t{delta}"); } - -#!csharp - -TValSeries test = new(); - -EmaCalc ma1 = new(test, 7, true); -TValSeries res1 = new(ma1); - -EmaCalc ma2 = new(test, 7, false); -TValSeries res2 = new(ma2); - -test.Add(new[]{1.0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0}); - -for (int i=0; i + /// Initializes a new instance of the Jma class with the specified parameters. + /// + /// The period over which to calculate the Jvolty. + /// The phase parameter for the JMA-style calculation. + /// + /// Thrown when period is less than 1. + /// + public Jmaxx(int period, int phase = 0) { if (period < 1) { throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); } - Period = period; - _vshort = vshort; - _vlong = 65; + _period = period; + _phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5); - _voltyShort = new CircularBuffer(vshort); - _vsumBuff = new CircularBuffer(_vlong); - _avoltyBuff = new CircularBuffer(2); + _vsumBuff = new CircularBuffer(10); + _avoltyBuff = new CircularBuffer(65); + _beta = 0.45 * (_period - 1) / (0.45 * (_period - 1) + 2); - Name = "JMA"; - WarmupPeriod = period * 2; - Init(); + WarmupPeriod = (int)_period * 2; + Name = $"JMA({period})"; } + /// + /// Initializes the Jma instance by setting up the initial state. + /// public override void Init() { - _upperBand = _lowerBand = _prevMa1 = _prevDet0 = _prevDet1 = _prevJma = 0.0; - _p_UpperBand = _p_LowerBand = _p_prevMa1 = _p_prevDet0 = _p_prevDet1 = _p_prevJma = 0.0; - _avoltyBuff.Clear(); - _avoltyBuff.Add(0, true); - _avoltyBuff.Add(0, true); base.Init(); + _upperBand = _lowerBand = 0.0; + _p_upperBand = _p_lowerBand = 0.0; + _len1 = Math.Max((Math.Log(Math.Sqrt(_period - 1)) / Math.Log(2.0)) + 2.0, 0); + _pow1 = Math.Max(_len1 - 2.0, 0.5); + _avoltyBuff.Clear(); + _vsumBuff.Clear(); } + /// + /// Manages the state of the Jma instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. protected override void ManageState(bool isNew) { if (isNew) { - _lastValidValue = Input.Value; _index++; - // Save current state - _p_UpperBand = _upperBand; - _p_LowerBand = _lowerBand; + _p_upperBand = _upperBand; + _p_lowerBand = _lowerBand; + _p_vSum = _vSum; _p_prevMa1 = _prevMa1; _p_prevDet0 = _prevDet0; _p_prevDet1 = _prevDet1; @@ -141,66 +160,73 @@ public class Jma1 : AbstractBase } else { - // Restore previous state - _upperBand = _p_UpperBand; - _lowerBand = _p_LowerBand; + _upperBand = _p_upperBand; + _lowerBand = _p_lowerBand; + _vSum = _p_vSum; _prevMa1 = _p_prevMa1; _prevDet0 = _p_prevDet0; _prevDet1 = _p_prevDet1; _prevJma = _p_prevJma; - } } + + /// + /// Performs the Jma calculation for the current value. + /// + /// + /// The calculated Jma value for the current input. + /// protected override double Calculation() { ManageState(Input.IsNew); + double price = Input.Value; if (_index == 1) { - _prevMa1 = _prevJma = Input.Value; - return Input.Value; + _upperBand = _lowerBand = price; } - double del1 = Input.Value - _upperBand; - double del2 = Input.Value - _lowerBand; + double del1 = price - _upperBand; + double del2 = price - _lowerBand; double volty = Math.Max(Math.Abs(del1), Math.Abs(del2)); - _voltyShort.Add(volty, Input.IsNew); - double vsum = _vsumBuff.Newest() + 0.1 * (volty - _voltyShort.Oldest()); - _vsumBuff.Add(vsum, Input.IsNew); + _vsumBuff.Add(volty, Input.IsNew); + _vSum += (_vsumBuff[^1] - _vsumBuff[0]) / 10; + _avoltyBuff.Add(_vSum, Input.IsNew); + double avgvolty = _avoltyBuff.Average(); - double avolty = 0; - for (int i = 0; i < _vsumBuff.Count; i++) { avolty += _vsumBuff[i]; } - avolty /= _vsumBuff.Count; + double rvolty = (avgvolty > 0) ? volty / avgvolty : 1; + rvolty = Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1)); - double rVolty = (avolty > 0) ? volty / avolty *20: 0; - double _len1 = Math.Max((Math.Log(Math.Sqrt(Period)) / Math.Log(2.0)) + 2.0, 0); - double _pow1 = Math.Max(_len1 - 2, 0.5); + double pow2 = Math.Pow(rvolty, _pow1); + double Kv = Math.Pow(_beta, Math.Sqrt(pow2)); - rVolty = Math.Clamp(rVolty, 1.0, Math.Pow(_len1, 1.0 / _pow1)); + _upperBand = (del1 >= 0) ? price : price - (Kv * del1); + _lowerBand = (del2 <= 0) ? price : price - (Kv * del2); - double _pow2 = Math.Pow(rVolty, _pow1); - double _beta = 0.45 * (Period - 1) / (0.45 * (Period - 1) + 2); - double len2 = Math.Sqrt(0.5 * (Period - 1)) * _len1; - double _Kv = Math.Pow (_beta, Math.Sqrt(_pow2)) *1.5; - _upperBand = (del1 > 0) ? Input.Value : Input.Value - (_Kv * del1); - _lowerBand = (del2 < 0) ? Input.Value : Input.Value - (_Kv * del2); - double alpha = Math.Pow(_beta, _pow2); - double ma1 = alpha * (_prevMa1 - Input.Value) + Input.Value; + + + + double alpha = Math.Pow(_beta, pow2); + double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1; _prevMa1 = ma1; - double det0 = _beta * (_prevDet0 - Input.Value + ma1) + Input.Value - ma1; + double det0 = (price - ma1) * (1 - _beta) + _beta * _prevDet0; _prevDet0 = det0; double ma2 = ma1 + _phase * det0; - double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - _prevJma)) + (alpha * alpha * _prevDet1 ); + double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha) ) + (alpha * alpha * _prevDet1); _prevDet1 = det1; double jma = _prevJma + det1; _prevJma = jma; + UpperBand = _upperBand; + LowerBand = _lowerBand; + Volty = volty; + IsHot = _index >= WarmupPeriod; return jma; } @@ -208,22 +234,17 @@ public class Jma1 : AbstractBase #!csharp -TSeries ma = Triangle; -TSeries re = TriangleJMA; +TSeries ma = Complex; +TSeries re = ComplexJMA; TSeries out1 = new(); -TSeries out2 = new(); -Jma calc = new(10); -Jma1 calc1 = new(10); + +Jmaxx calc = new(10); + foreach (var value in ma) { out1.Add(calc.Calc(value)); } -foreach (var value in ma) { out2.Add(calc1.Calc(value)); } + Plot plt = new(); var sigplot = plt.Add.Signal(ma.v.ToArray()[60..80]); var jmaplot = plt.Add.Signal(re.v.ToArray()[60..80]); sigplot.Color = ScottPlot.Colors.Red; sigplot.LineWidth = 2; jmaplot.LineWidth = 3; -//var jma1plot = plt.Add.Signal(out1.v.ToArray()[60..80]); jma1plot.Color = ScottPlot.Colors.Purple; jma1plot.LineWidth = 3; -var jma2plot = plt.Add.Signal(out2.v.ToArray()[60..80]); jma2plot.Color = ScottPlot.Colors.Blue; jma2plot.LineWidth = 3; +var jma1plot = plt.Add.Signal(out1.v.ToArray()[60..80]); jma1plot.Color = ScottPlot.Colors.Purple; jma1plot.LineWidth = 3; + plt.Display(); - -#!csharp - -#r "nuget: Plotly.net.Interactive" -using Plotly.NET.Interactive; diff --git a/notebooks/means.dib b/notebooks/means.dib index 16530f57..3ac96ef8 100644 --- a/notebooks/means.dib +++ b/notebooks/means.dib @@ -10,15 +10,19 @@ QuanTAlib.Formatters.Initialize(); #!csharp -Sma ma1 = new(6); -Gmean ma2 = new (6); -Hmean ma3 = new (6); +TSeries input = new(); +Beta ma1 = new (6); +Beta 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 allPoints = new List(); - if (CurrentChart == null) return; - - Graphics gr = args.Graphics; - var mainWindow = CurrentChart.MainWindow; - var converter = mainWindow.CoordinatesConverter; - var clientRect = mainWindow.ClientRectangle; - - gr.SetClip(clientRect); - DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); - DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); - - int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; - int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); - - for (int i = rightIndex; i < leftIndex; i++) - { - 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); - allPoints.Add(point); - } - - if (allPoints.Count > 1) - { - DrawSmoothCombinedCurve(gr, allPoints, this.Count - MovingAverage.WarmupPeriod - rightIndex); - } - } - - private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) - { - 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)Tension); - } - - // 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)Tension); - } - } - } - - protected void DrawText(Graphics gr, string text, Rectangle clientRect) - { - Font font = new Font("Inter", 8); - SizeF textSize = gr.MeasureString(text, font); - RectangleF textRect = new RectangleF(clientRect.Left + 5, - clientRect.Bottom - textSize.Height - 10, - textSize.Width + 10, textSize.Height + 10); - gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); - gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); - } - - private DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) - { - return lineStyle switch - { - LineStyle.Solid => DashStyle.Solid, - LineStyle.Dash => DashStyle.Dash, - LineStyle.Dot => DashStyle.Dot, - LineStyle.DashDot => DashStyle.DashDot, - _ => DashStyle.Solid, - }; - } -} \ No newline at end of file diff --git a/quantower/Averages/AfirmaIndicator.cs b/quantower/Averages/AfirmaIndicator.cs index b7fb250d..a094c320 100644 --- a/quantower/Averages/AfirmaIndicator.cs +++ b/quantower/Averages/AfirmaIndicator.cs @@ -1,25 +1,82 @@ +using System.Drawing; using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class AfirmaIndicator : IndicatorBase +public class AfirmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Taps (number of weights)", sortIndex: 1, 1, 2000, 1, 0)] + public int Taps { get; set; } = 6; - [InputParameter("Alpha", sortIndex: 2, 0.01, 0.99, 0.01, 2)] - public double Alpha { get; set; } = 0.1; + [InputParameter("Periods for lowpass cutoff", sortIndex: 2, 1, 2000, 1, 0)] + public int Periods { get; set; } = 6; + + [InputParameter("Window Type", sortIndex: 3, variants: [ + "Rectangular", Afirma.WindowType.Rectangular, + "Hanning", Afirma.WindowType.Hanning1, + "Hamming", Afirma.WindowType.Hanning2, + "Blackman", Afirma.WindowType.Blackman, + "Blackman-Harris", Afirma.WindowType.BlackmanHarris + ])] + public Afirma.WindowType Window { get; set; } = Afirma.WindowType.Hanning1; + + [InputParameter("Data source", sortIndex: 4, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Afirma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"AFIRMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods + Taps; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; public AfirmaIndicator() { - Name = "AFIRMA - Adaptive Filtering Integrated Recursive Moving Average"; - Description = "Adaptive Filtering Integrated Recursive Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "AFIRMA - Adaptive Finite Impulse Response Moving Average"; + Description = "Adaptive Finite Impulse Response Moving Average with ARMA component"; + + Series = new(name: $"AFIRMA {Taps}:{Periods}:{Window}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Afirma(period: Period, alpha: Alpha); + ma = new Afirma(periods: Periods, taps: Taps, window: Window); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + Series!.SetValue(result.Value); + } + + public override string ShortName => $"AFIRMA {Taps}:{Periods}:{Window}:{SourceName}"; + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); + } +} + diff --git a/quantower/Averages/AlmaIndicator.cs b/quantower/Averages/AlmaIndicator.cs index 251ad0f1..50b49d30 100644 --- a/quantower/Averages/AlmaIndicator.cs +++ b/quantower/Averages/AlmaIndicator.cs @@ -1,28 +1,75 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class AlmaIndicator : IndicatorBase +namespace QuanTAlib; + +public class AlmaIndicator : Indicator, IWatchlistIndicator { [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] public int Period { get; set; } = 10; - [InputParameter("Offset", sortIndex: 5)] - public double Offset = 0.85; + [InputParameter("Offset", sortIndex: 2, minimum: 0, maximum: 1, decimalPlaces: 2)] + public double Offset { get; set; } = 0.85; + + [InputParameter("Sigma", sortIndex: 3, minimum: 0, maximum: 100, decimalPlaces: 1)] + public double Sigma { get; set; } = 6.0; + + [InputParameter("Data source", sortIndex: 4, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; - [InputParameter("Sigma", sortIndex: 6)] - public double Sigma = 6.0; private Alma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"ALMA {Period} : {Offset:F2} : {Sigma:F0} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Period; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public AlmaIndicator() : base() + public override string ShortName => $"ALMA {Period}:{Offset:F2}:{Sigma:F1}:{SourceName}"; + + public AlmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "ALMA - Arnaud Legoux Moving Average"; + Description = "Arnaud Legoux Moving Average"; + Series = new(name: $"ALMA {Period}:{Offset:F2}:{Sigma:F0}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); ma = new Alma(period: Period, offset: Offset, sigma: Sigma); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/DemaIndicator.cs b/quantower/Averages/DemaIndicator.cs index 185cf6a5..7ca1824f 100644 --- a/quantower/Averages/DemaIndicator.cs +++ b/quantower/Averages/DemaIndicator.cs @@ -1,22 +1,69 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class DemaIndicator : IndicatorBase +namespace QuanTAlib; + +public class DemaIndicator : Indicator, IWatchlistIndicator { [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] public int Period { get; set; } = 10; - private Dema? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"DEMA {Period} : {SourceName}"; - public DemaIndicator() : base() + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Dema? ma; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Period; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public override string ShortName => $"DEMA {Period}:{SourceName}"; + + public DemaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "DEMA - Double Exponential Moving Average"; + Description = "A faster-responding moving average that reduces lag by applying the EMA twice."; + Series = new(name: $"DEMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); ma = new Dema(period: Period); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/DsmaIndicator.cs b/quantower/Averages/DsmaIndicator.cs index 9bf1dc6d..8c22a636 100644 --- a/quantower/Averages/DsmaIndicator.cs +++ b/quantower/Averages/DsmaIndicator.cs @@ -1,26 +1,73 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class DsmaIndicator : IndicatorBase +namespace QuanTAlib; + +public class DsmaIndicator : Indicator, IWatchlistIndicator { [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] public int Period { get; set; } = 10; + [InputParameter("Scale factor", sortIndex: 2, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] public double Scale { get; set; } = 0.5; - private Dsma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"DSMA {Period} : {Scale:F2} : {SourceName}"; + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; - public DsmaIndicator() : base() + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Dsma? ma; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths { get; private set; } + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public override string ShortName => $"DSMA {Period}:{Scale:F2}:{SourceName}"; + + public DsmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "DSMA - Deviation Scaled Moving Average"; + Description = "A moving average that adjusts its responsiveness based on price deviations from the mean."; + Series = new(name: $"DSMA {Period}:{Scale:F2}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { ma = new Dsma(Period, Scale); MinHistoryDepths = ma.WarmupPeriod; - base.InitIndicator(); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/DwmaIndicator.cs b/quantower/Averages/DwmaIndicator.cs index 5b80c398..6b195215 100644 --- a/quantower/Averages/DwmaIndicator.cs +++ b/quantower/Averages/DwmaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class DwmaIndicator : IndicatorBase +namespace QuanTAlib; + +public class DwmaIndicator : Indicator, IWatchlistIndicator { [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] public int Period { get; set; } = 10; + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + private Dwma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"DWMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Period; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"DWMA {Period}:{SourceName}"; - public DwmaIndicator() : base() + public DwmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "DWMA - Double Weighted Moving Average"; + Description = "A moving average that applies double weighting to recent prices for increased responsiveness."; + Series = new(name: $"DWMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { ma = new Dwma(Period); - base.InitIndicator(); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/EmaIndicator.cs b/quantower/Averages/EmaIndicator.cs index 7adecd30..3e16cd9b 100644 --- a/quantower/Averages/EmaIndicator.cs +++ b/quantower/Averages/EmaIndicator.cs @@ -1,27 +1,71 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class EmaIndicator : IndicatorBase +namespace QuanTAlib; + +public class EmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 10; + [InputParameter("Use SMA for warmup period", sortIndex: 2)] + public bool UseSMA { get; set; } = false; - [InputParameter("Use SMA for warmup", sortIndex: 5)] - public bool UseSma { get; set; } = false; + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Ema? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"EMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public EmaIndicator() : base() + public override string ShortName => $"EMA {Periods}:{SourceName}"; + + public EmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "EMA - Exponential Moving Average"; Description = "Exponential Moving Average"; + Series = new(name: $"EMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Ema(period: Period, useSma: UseSma); + ma = new Ema(Periods, useSma: UseSMA); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/EpmaIndicator.cs b/quantower/Averages/EpmaIndicator.cs index 1a92d33c..39aad553 100644 --- a/quantower/Averages/EpmaIndicator.cs +++ b/quantower/Averages/EpmaIndicator.cs @@ -1,23 +1,69 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class EpmaIndicator : IndicatorBase +namespace QuanTAlib; + +public class EpmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 10; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Epma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"EPMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public EpmaIndicator() : base() + public override string ShortName => $"EPMA {Periods}:{SourceName}"; + + public EpmaIndicator() { - Name = "EPMA - Endpoint Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "EPMA - Exponential Percentage Moving Average"; + Description = "Exponential Percentage Moving Average"; + Series = new(name: $"EPMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Epma(period: Period); + ma = new Epma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/FramaIndicator.cs b/quantower/Averages/FramaIndicator.cs index 865f4a53..ddcceede 100644 --- a/quantower/Averages/FramaIndicator.cs +++ b/quantower/Averages/FramaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class FramaIndicator : IndicatorBase +namespace QuanTAlib; + +public class FramaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 10; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Frama? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"FRAMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods * 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"FRAMA {Periods}:{SourceName}"; - public FramaIndicator() : base() + public FramaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "FRAMA - Fractal Adaptive Moving Average"; + Description = "Fractal Adaptive Moving Average"; + Series = new(name: $"FRAMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Frama(Period); - base.InitIndicator(); + ma = new Frama(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/FwmaIndicator.cs b/quantower/Averages/FwmaIndicator.cs index b4a9a389..bb9c89a1 100644 --- a/quantower/Averages/FwmaIndicator.cs +++ b/quantower/Averages/FwmaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class FwmaIndicator : IndicatorBase +namespace QuanTAlib; + +public class FwmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 10; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Fwma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"FWMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"FWMA {Periods}:{SourceName}"; - public FwmaIndicator() : base() + public FwmaIndicator() { - Name = "FWMA - Fibonacci-Weighted Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "FWMA - Fibonacci Weighted Moving Average"; + Description = "Fibonacci Weighted Moving Average"; + Series = new(name: $"FWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Fwma(Period); - base.InitIndicator(); + ma = new Fwma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/GmaIndicator.cs b/quantower/Averages/GmaIndicator.cs index 5d85e499..f4cfce92 100644 --- a/quantower/Averages/GmaIndicator.cs +++ b/quantower/Averages/GmaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; -using QuanTAlib; +using System.Drawing; +using TradingPlatform.BusinessLayer; -public class GmaIndicator : IndicatorBase +namespace QuanTAlib; + +public class GmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 10; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Gma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"GMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"GMA {Periods}:{SourceName}"; - public GmaIndicator() : base() + public GmaIndicator() { - Name = "GMA - Gaussian-Weighted Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "GMA - Gaussian Moving Average"; + Description = "Gaussian Moving Average"; + Series = new(name: $"GMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Gma(Period); - base.InitIndicator(); + ma = new Gma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/HmaIndicator.cs b/quantower/Averages/HmaIndicator.cs index 2dd2b472..e9241249 100644 --- a/quantower/Averages/HmaIndicator.cs +++ b/quantower/Averages/HmaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class HmaIndicator : IndicatorBase +public class HmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 10; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Hma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"HMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods + (int)Math.Sqrt(Periods) - 1; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"HMA {Periods}:{SourceName}"; - public HmaIndicator() : base() + public HmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "HMA - Hull Moving Average"; + Description = "Hull Moving Average"; + Series = new(name: $"HMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Hma(Period); - base.InitIndicator(); + ma = new Hma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/HtitIndicator.cs b/quantower/Averages/HtitIndicator.cs index 8d0f4009..411d8afa 100644 --- a/quantower/Averages/HtitIndicator.cs +++ b/quantower/Averages/HtitIndicator.cs @@ -1,21 +1,66 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class HtitIndicator : IndicatorBase +public class HtitIndicator : Indicator, IWatchlistIndicator { - private Htit? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"HTIT : {SourceName}"; + [InputParameter("Data source", sortIndex: 1, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; - public HtitIndicator() : base() + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Htit? ma; + protected LineSeries? Series; + protected string? SourceName; + public static int MinHistoryDepths => 12; // Based on WarmupPeriod in Htit + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public override string ShortName => $"HTIT:{SourceName}"; + + public HtitIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "HTIT - Hilbert Transform Instantaneous Trendline"; + Description = "Hilbert Transform Instantaneous Trendline (Note: This indicator may not be fully functional)"; + Series = new(name: "HTIT", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { ma = new Htit(); - MinHistoryDepths = ma.WarmupPeriod; - base.InitIndicator(); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/HwmaIndicator.cs b/quantower/Averages/HwmaIndicator.cs index e777af3c..de81b817 100644 --- a/quantower/Averages/HwmaIndicator.cs +++ b/quantower/Averages/HwmaIndicator.cs @@ -1,33 +1,85 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class HwmaIndicator : IndicatorBase +public class HwmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("nA - smoothed series", sortIndex: 5, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] - public double nA { get; set; } = 0.18; + [InputParameter("Periods (only when nA=nB=nC=0)", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 10; - [InputParameter("nB - assess the trend (from 0 to 1)", sortIndex: 6, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] - public double nB { get; set; } = 0.1; + [InputParameter("nA", sortIndex: 2, 0, 1, 0.01, 2)] + public double NA { get; set; } = 0; - [InputParameter("nC - assess seasonality (from 0 to 1)", sortIndex: 7, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] - public double nC { get; set; } = 0.1; + [InputParameter("nB", sortIndex: 3, 0, 1, 0.01, 2)] + public double NB { get; set; } = 0; + + [InputParameter("nC", sortIndex: 4, 0, 1, 0.01, 2)] + public double NC { get; set; } = 0; + + [InputParameter("Data source", sortIndex: 5, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Hwma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"HWMA {nA:F2} : {nB:F2} : {nC:F2} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"HWMA {Periods}:{NA}:{NB}:{NC}:{SourceName}"; - public HwmaIndicator() : base() + public HwmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "HWMA - Holt-Winter Moving Average"; + Description = "Holt-Winter Moving Average"; + Series = new(name: $"HWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - //nA = 2 / (1 + (double)Period); - //nB = 1 / (double)Period; - //nC = 1 / (double)Period; - ma = new Hwma(nA: nA, nB: nB, nC: nC); - base.InitIndicator(); + if (NA == 0 && NB == 0 && NC == 0) + { + ma = new Hwma(Periods); + } + else + { + ma = new Hwma(Periods, NA, NB, NC); + } + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/JmaIndicator.cs b/quantower/Averages/JmaIndicator.cs index 88a0a2ea..5050d3da 100644 --- a/quantower/Averages/JmaIndicator.cs +++ b/quantower/Averages/JmaIndicator.cs @@ -1,26 +1,75 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class JmaIndicator : IndicatorBase +public class JmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 10; [InputParameter("Phase", sortIndex: 2, -100, 100, 1, 0)] public int Phase { get; set; } = 0; + + [InputParameter("Beta factor", sortIndex: 3, minimum: 0, maximum:5 , increment: 0.01, decimalPlaces: 2)] + public double Factor { get; set; } = 0.45; + + [InputParameter("Data source", sortIndex: 4, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + private Jma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"JMA {Period} : {Phase} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Math.Max(65,Periods * 2); + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"JMA {Periods}:{Phase}:{Factor:F2}:{SourceName}"; - public JmaIndicator() : base() + public JmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "JMA - Jurik Moving Average"; + Description = "Jurik Moving Average (Note: This indicator may have consistency issues)"; + Series = new(name: $"JMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Jma(period: Period, phase: (double)Phase); - base.InitIndicator(); + ma = new Jma(period: Periods, phase: Phase, factor: Factor); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/KamaIndicator.cs b/quantower/Averages/KamaIndicator.cs index 21587ab1..dfc14a90 100644 --- a/quantower/Averages/KamaIndicator.cs +++ b/quantower/Averages/KamaIndicator.cs @@ -1,28 +1,75 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class KamaIndicator : IndicatorBase +public class KamaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 10; - [InputParameter("Fast", sortIndex: 2, 1, 2000, 1, 0)] + [InputParameter("Fast", sortIndex: 2, 1, 100, 1, 0)] public int Fast { get; set; } = 2; - [InputParameter("Slow", sortIndex: 3, 1, 2000, 1, 0)] + + [InputParameter("Slow", sortIndex: 3, 1, 100, 1, 0)] public int Slow { get; set; } = 30; + + [InputParameter("Data source", sortIndex: 4, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + private Kama? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"KAMA {Period} : {Fast} : {Slow} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"KAMA {Periods}:{Fast}:{Slow}:{SourceName}"; - public KamaIndicator() : base() + public KamaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "KAMA - Kaufman's Adaptive Moving Average"; + Description = "Kaufman's Adaptive Moving Average"; + Series = new(name: $"KAMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Kama(Period, Fast, Slow); - base.InitIndicator(); + ma = new Kama(Periods, Fast, Slow); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/LtmaIndicator.cs b/quantower/Averages/LtmaIndicator.cs index d786d0bb..77f2f868 100644 --- a/quantower/Averages/LtmaIndicator.cs +++ b/quantower/Averages/LtmaIndicator.cs @@ -1,23 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class LtmaIndicator : IndicatorBase +public class LtmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Gamma", sortIndex: 1, 0, 1, 0.01, 2)] - public double Gamma { get; set; } = 0.10; + [InputParameter("Gamma", sortIndex: 1, 0.01, 1, 0.01, 2)] + public double Gamma { get; set; } = 0.1; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Ltma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"Laguerre {Gamma:F2} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public static int MinHistoryDepths => 4; // Based on WarmupPeriod in Ltma + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public LtmaIndicator() : base() + public override string ShortName => $"LTMA {Gamma}:{SourceName}"; + + public LtmaIndicator() { - Name = "LTMA - Laguerre Transform Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "LTMA - Laguerre Time Moving Average"; + Description = "Laguerre Time Moving Average"; + Series = new(name: $"LTMA {Gamma}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Ltma(gamma: Gamma); - base.InitIndicator(); + ma = new Ltma(Gamma); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/MaafIndicator.cs b/quantower/Averages/MaafIndicator.cs index 6cb7128c..fc474704 100644 --- a/quantower/Averages/MaafIndicator.cs +++ b/quantower/Averages/MaafIndicator.cs @@ -1,26 +1,72 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class MaafIndicator : IndicatorBase +public class MaafIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 39; + [InputParameter("Periods", sortIndex: 1, 3, 1000, 1, 0)] + public int Periods { get; set; } = 10; - [InputParameter("Threshold", sortIndex: 5, minimum: 0, maximum: 1, increment: 0.001, decimalPlaces:3)] - public double Threshold = 0.002; + [InputParameter("Threshold", sortIndex: 2, 0.0001, 0.1, 0.0001, 4)] + public double Threshold { get; set; } = 0.002; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Maaf? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"MAAF {Period} : {Threshold:F2} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public MaafIndicator() : base() + public override string ShortName => $"MAAF {Periods}:{Threshold}:{SourceName}"; + + public MaafIndicator() { - Name = "MAAF - Median-Average Adaptive Filter"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "MAAF - Median Adaptive Averaging Filter"; + Description = "Median Adaptive Averaging Filter (Note: This indicator may have consistency issues)"; + Series = new(name: $"MAAF {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Maaf(Period: Period, Threshold: Threshold); + ma = new Maaf(Periods, Threshold); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/MacdIndicator.cs b/quantower/Averages/MacdIndicator.cs new file mode 100644 index 00000000..56365f3f --- /dev/null +++ b/quantower/Averages/MacdIndicator.cs @@ -0,0 +1,149 @@ +using System.Diagnostics.Metrics; +using System.Drawing; +using System.Drawing.Drawing2D; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class MacdIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Slow EMA", sortIndex: 1, 1, 1000, 1, 0)] + public int Slow { get; set; } = 26; + + [InputParameter("Fast EMA", sortIndex: 2, 1, 2000, 1, 0)] + public int Fast { get; set; } = 12; + + [InputParameter("Signal line", sortIndex: 3, 1, 2000, 1, 0)] + public int Signal { get; set; } = 9; + + [InputParameter("Use SMA for warmup period", sortIndex: 2)] + public bool UseSMA { get; set; } = false; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Ema? slow_ma; + private Ema? fast_ma; + private Ema? signal_ma; + private Slope? histSlope; + protected LineSeries? MainSeries; + protected LineSeries? SignalSeries; + protected LineSeries? HistogramSeries; + protected LineSeries? HistSlopeSeries; + + protected string? SourceName; + public int MinHistoryDepths => Slow; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public override string ShortName => $"MACD {Slow}:{Fast}:{Signal}"; + + public MacdIndicator() + { + OnBackGround = true; + SeparateWindow = true; + SourceName = Source.ToString(); + Name = "MACD - Moving Average Convergence Divergence"; + Description = "MACD"; + MainSeries = new(name: $"MAIN", color: Color.Blue, width: 2, style: LineStyle.Solid); + SignalSeries = new(name: $"SIGNAL", color: Color.Yellow, width: 2, style: LineStyle.Solid); + HistogramSeries = new(name: $"HISTOGRAM", color: Color.White, width: 2, style: LineStyle.Solid); + HistSlopeSeries = new(name: $"SLOPE", color: Color.Transparent, width: 2, style: LineStyle.Solid); + HistSlopeSeries.Visible = false; + + AddLineSeries(MainSeries); + AddLineSeries(SignalSeries); + AddLineSeries(HistogramSeries); + AddLineSeries(HistSlopeSeries); + } + + protected override void OnInit() + { + slow_ma = new(Slow, useSma: UseSMA); + fast_ma = new(Fast, useSma: UseSMA); + signal_ma = new(Signal, useSma: UseSMA); + histSlope = new(2); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + slow_ma!.Calc(input); + fast_ma!.Calc(input); + double main = fast_ma.Value - slow_ma.Value; + double signal = signal_ma!.Calc(main); + double histogram = main - signal; + histSlope!.Calc(histogram); + + MainSeries!.SetValue(main); + MainSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + SignalSeries!.SetValue(signal); + SignalSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + HistogramSeries!.SetValue(histogram); + HistogramSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + HistSlopeSeries!.SetValue(histSlope.Value); + HistSlopeSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } +#pragma warning disable CA1416 // Validate platform compatibility + + public override void OnPaintChart(PaintChartEventArgs args) + { + + Graphics gr = args.Graphics; + gr.SmoothingMode = SmoothingMode.AntiAlias; + var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), this.HistoricalData.Time(this!.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), this.HistoricalData.Time(0) }.Min(); + int leftIndex = (int)this.HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)this.HistoricalData.GetIndexByTime(rightTime.Ticks); + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(this.HistoricalData.Time(i)); + int barY = (int)converter.GetChartY(HistogramSeries![i]*2.0); + int barY0 = (int)converter.GetChartY(0); + int HistBarWidth = this.CurrentChart.BarsWidth - 2; + + Brush lowGreen = new SolidBrush(Color.FromArgb(255, 0, 100, 0)); + Brush highGreen = new SolidBrush(Color.FromArgb(255, 50, 255, 50)); + Brush lowRed = new SolidBrush(Color.FromArgb(255, 100, 0, 0)); + Brush highRed = new SolidBrush(Color.FromArgb(255, 255, 50, 50)); + + if (HistogramSeries[i] > 0) + { + Brush col = HistSlopeSeries![i] > 0 ? highGreen : lowGreen; + gr.FillRectangle(col, barX, barY, HistBarWidth, Math.Abs(barY - barY0)); + } + else + { + Brush col = HistSlopeSeries![i] < 0 ? highRed : lowRed; + gr.FillRectangle(col, barX, barY0, HistBarWidth, Math.Abs(barY0 - barY)); + } + + } + + this.PaintSmoothCurve(args, MainSeries!, slow_ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.3); + this.PaintSmoothCurve(args, SignalSeries!, slow_ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + base.OnPaintChart(args); + } +} + diff --git a/quantower/Averages/MamaIndicator.cs b/quantower/Averages/MamaIndicator.cs index d3e14f30..39b31438 100644 --- a/quantower/Averages/MamaIndicator.cs +++ b/quantower/Averages/MamaIndicator.cs @@ -1,25 +1,78 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class MamaIndicator : IndicatorBase +public class MamaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Fast limit", sortIndex: 2, 0, 1, 0.01, 2)] - public double Fast { get; set; } = 0.4; - [InputParameter("Slow limit", sortIndex: 3, 0, 1, 0.01, 2)] - public double Slow { get; set; } = 0.04; + [InputParameter("Fast Limit", sortIndex: 1, 0.01, 1, 0.01, 2)] + public double FastLimit { get; set; } = 0.5; + + [InputParameter("Slow Limit", sortIndex: 2, 0.01, 1, 0.01, 2)] + public double SlowLimit { get; set; } = 0.05; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + private Mama? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"MAMA : {Fast} : {Slow} : {SourceName}"; + protected LineSeries? MamaSeries; + protected LineSeries? FamaSeries; + protected string? SourceName; + public static int MinHistoryDepths => 6; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"MAMA {FastLimit}:{SlowLimit}:{SourceName}"; - public MamaIndicator() : base() + public MamaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "MAMA - MESA Adaptive Moving Average"; + Description = "MESA Adaptive Moving Average"; + MamaSeries = new(name: "MAMA", color: Color.Yellow, width: 2, style: LineStyle.Solid); + FamaSeries = new(name: "FAMA", color: Color.Red, width: 2, style: LineStyle.Solid); + AddLineSeries(MamaSeries); + AddLineSeries(FamaSeries); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Mama(Fast, Slow); - base.InitIndicator(); + ma = new Mama(FastLimit, SlowLimit); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + MamaSeries!.SetValue(result.Value); + MamaSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + FamaSeries!.SetValue(ma.Fama.Value); + FamaSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, MamaSeries!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.PaintSmoothCurve(args, FamaSeries!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/MgdiIndicator.cs b/quantower/Averages/MgdiIndicator.cs index a9ed0bb0..7a865b5b 100644 --- a/quantower/Averages/MgdiIndicator.cs +++ b/quantower/Averages/MgdiIndicator.cs @@ -1,28 +1,72 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class MgdiIndicator : IndicatorBase +public class MgdiIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; - [InputParameter("k Factor", sortIndex: 2, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] - public double kfactor { get; set; } = 0.6; + [InputParameter("K-Factor", sortIndex: 2, 0.1, 2, 0.1, 1)] + public double KFactor { get; set; } = 0.6; + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Mgdi? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"MGDI {Period} : {kfactor:F2} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"MGDI {Periods}:{KFactor}:{SourceName}"; - public MgdiIndicator() : base() + public MgdiIndicator() { - Name = "MGDI - McGinley Dynamic Index"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "MGDI - McGinley Dynamic Indicator"; + Description = "McGinley Dynamic Indicator"; + Series = new(name: $"MGDI {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Mgdi(period: Period, kFactor: kfactor); - base.InitIndicator(); + ma = new Mgdi(Periods, KFactor); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/MmaIndicator.cs b/quantower/Averages/MmaIndicator.cs index bf174265..c7701c74 100644 --- a/quantower/Averages/MmaIndicator.cs +++ b/quantower/Averages/MmaIndicator.cs @@ -1,23 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class MmaIndicator : IndicatorBase +public class MmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Mma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"MMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public MmaIndicator() : base() + public override string ShortName => $"MMA {Periods}:{SourceName}"; + + public MmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "MMA - Modified Moving Average"; + Description = "Modified Moving Average"; + Series = new(name: $"MMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Mma(period: Period); + ma = new Mma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/PwmaIndicator.cs b/quantower/Averages/PwmaIndicator.cs new file mode 100644 index 00000000..14eb349d --- /dev/null +++ b/quantower/Averages/PwmaIndicator.cs @@ -0,0 +1,69 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class PwmaIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Pwma? ma; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public override string ShortName => $"PWMA {Periods}:{SourceName}"; + + public PwmaIndicator() + { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "PWMA - Pascal's Weighted Moving Average"; + Description = "Pascal's Weighted Moving Average"; + Series = new(name: $"PWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); + } + + protected override void OnInit() + { + ma = new Pwma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); + } +} diff --git a/quantower/Averages/QemaIndicator.cs b/quantower/Averages/QemaIndicator.cs index 3434249e..d9a114eb 100644 --- a/quantower/Averages/QemaIndicator.cs +++ b/quantower/Averages/QemaIndicator.cs @@ -1,30 +1,78 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class QemaIndicator : IndicatorBase +public class QemaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("alpha 1", sortIndex: 1, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] - public double k1 { get; set; } = 0.2; + [InputParameter("K1", sortIndex: 1, 0.01, 1, 0.01, 2)] + public double K1 { get; set; } = 0.2; + + [InputParameter("K2", sortIndex: 2, 0.01, 1, 0.01, 2)] + public double K2 { get; set; } = 0.2; + + [InputParameter("K3", sortIndex: 3, 0.01, 1, 0.01, 2)] + public double K3 { get; set; } = 0.2; + + [InputParameter("K4", sortIndex: 4, 0.01, 1, 0.01, 2)] + public double K4 { get; set; } = 0.2; + + [InputParameter("Data source", sortIndex: 5, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; - [InputParameter("alpha 2", sortIndex: 2, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] - public double k2 { get; set; } = 0.3; - [InputParameter("alpha 3", sortIndex: 3, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] - public double k3 { get; set; } = 0.4; - [InputParameter("alpha 4", sortIndex: 4, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] - public double k4 { get; set; } = 0.5; private Qema? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"QEMA {k1:F2} : {k2:F2} : {k3:F2} : {k4:F2} :{SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => (int)((2 - Math.Min(Math.Min(K1, K2), Math.Min(K3, K4))) / Math.Min(Math.Min(K1, K2), Math.Min(K3, K4))); + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public QemaIndicator() : base() + public override string ShortName => $"QEMA {K1},{K2},{K3},{K4}:{SourceName}"; + + public QemaIndicator() { - Name = "QEMA - Quad Exponential Moving Average"; - Description = "Quad Exponential Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "QEMA - Quadruple Exponential Moving Average"; + Description = "Quadruple Exponential Moving Average"; + Series = new(name: $"QEMA {K1},{K2},{K3},{K4}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Qema(k1, k2, k3, k4); + ma = new Qema(K1, K2, K3, K4); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/RemaIndicator.cs b/quantower/Averages/RemaIndicator.cs index 26193da4..3197b9bf 100644 --- a/quantower/Averages/RemaIndicator.cs +++ b/quantower/Averages/RemaIndicator.cs @@ -1,26 +1,72 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class RemaIndicator : IndicatorBase +public class RemaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; - [InputParameter("Regularization Factor", sortIndex: 2, minimum: 0, maximum: 2.5, increment: 0.1, decimalPlaces: 1)] + [InputParameter("Lambda", sortIndex: 2, 0, 1, 0.01, 2)] public double Lambda { get; set; } = 0.5; - private Rema? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"REMA {Period} : {Lambda:F2} : {SourceName}"; + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; - public RemaIndicator() : base() + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Rema? ma; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public override string ShortName => $"REMA {Periods}:{Lambda}:{SourceName}"; + + public RemaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "REMA - Regularized Exponential Moving Average"; + Description = "Regularized Exponential Moving Average"; + Series = new(name: $"REMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Rema(period: Period, lambda: Lambda); + ma = new Rema(Periods, Lambda); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/RmaIndicator.cs b/quantower/Averages/RmaIndicator.cs index a89e15a6..b0cf4306 100644 --- a/quantower/Averages/RmaIndicator.cs +++ b/quantower/Averages/RmaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class RmaIndicator : IndicatorBase +public class RmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Rma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"RMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods * 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"RMA {Periods}:{SourceName}"; - public RmaIndicator() : base() + public RmaIndicator() { - Name = "RMA - wildeR Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "RMA - Relative Moving Average (Wilder's Moving Average)"; + Description = "Relative Moving Average, also known as Wilder's Moving Average"; + Series = new(name: $"RMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Rma(Period); - base.InitIndicator(); + ma = new Rma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/SinemaIndicator.cs b/quantower/Averages/SinemaIndicator.cs index 2f3b231f..0aa8feb8 100644 --- a/quantower/Averages/SinemaIndicator.cs +++ b/quantower/Averages/SinemaIndicator.cs @@ -1,23 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class SinemaIndicator : IndicatorBase +public class SinemaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Sinema? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"SINEMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public SinemaIndicator() : base() + public override string ShortName => $"SINEMA {Periods}:{SourceName}"; + + public SinemaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "SINEMA - Sine-Weighted Moving Average"; + Description = "Sine-Weighted Moving Average"; + Series = new(name: $"SINEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Sinema(Period); - base.InitIndicator(); + ma = new Sinema(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/SmaIndicator.cs b/quantower/Averages/SmaIndicator.cs index 3cbf26fa..692b5545 100644 --- a/quantower/Averages/SmaIndicator.cs +++ b/quantower/Averages/SmaIndicator.cs @@ -1,24 +1,72 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class SmaIndicator : IndicatorBase +public class SmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Period { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Sma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"SMA {Period} : {SourceName}"; + private Mape? error; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Period; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - - public SmaIndicator() : base() + public SmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "SMA - Simple Moving Average"; + Description = "Simple Moving Average"; + Series = new(name: $"SMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { ma = new Sma(Period); - base.InitIndicator(); + error = new(Period); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + error!.Calc(input, result); + + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + Series!.SetValue(result.Value); + } + + public override string ShortName => $"SMA {Period}:{SourceName}"; + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, error!.Value.ToString()); } } diff --git a/quantower/Averages/SmmaIndicator.cs b/quantower/Averages/SmmaIndicator.cs index a7516e22..befb1816 100644 --- a/quantower/Averages/SmmaIndicator.cs +++ b/quantower/Averages/SmmaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class SmmaIndicator : IndicatorBase +public class SmmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Smma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"SMMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"SMMA {Periods}:{SourceName}"; - public SmmaIndicator() : base() + public SmmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "SMMA - Smoothed Moving Average"; + Description = "Smoothed Moving Average"; + Series = new(name: $"SMMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Smma(Period); - base.InitIndicator(); + ma = new Smma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/T3Indicator.cs b/quantower/Averages/T3Indicator.cs index 5b8f4766..7ab6d14e 100644 --- a/quantower/Averages/T3Indicator.cs +++ b/quantower/Averages/T3Indicator.cs @@ -1,29 +1,75 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class T3Indicator : IndicatorBase +public class T3Indicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; - [InputParameter("Vfactor", sortIndex: 2, 0, 1, 0.01, 2)] - public double Vfactor { get; set; } = 0.62; + [InputParameter("Volume Factor", sortIndex: 2, 0, 1, 0.01, 2)] + public double VolumeFactor { get; set; } = 0.7; - [InputParameter("Use SMA for warmup", sortIndex: 3)] - public bool UseSma { get; set; } = false; + [InputParameter("Use SMA", sortIndex: 3)] + public bool UseSma { get; set; } = true; + + [InputParameter("Data source", sortIndex: 4, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private T3? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"T3 {Period} : {Vfactor:F2} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public T3Indicator() : base() + public override string ShortName => $"T3 {Periods}:{VolumeFactor}:{UseSma}:{SourceName}"; + + public T3Indicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "T3 - Tillson T3 Moving Average"; + Description = "Tillson T3 Moving Average"; + Series = new(name: $"T3 {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new T3(period: Period, vfactor: Vfactor, useSma: UseSma); - base.InitIndicator(); + ma = new T3(Periods, VolumeFactor, UseSma); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/TemaIndicator.cs b/quantower/Averages/TemaIndicator.cs index 57d50da3..23862874 100644 --- a/quantower/Averages/TemaIndicator.cs +++ b/quantower/Averages/TemaIndicator.cs @@ -1,23 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class TemaIndicator : IndicatorBase +public class TemaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Tema? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"TEMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => (int)Math.Ceiling(-Periods * Math.Log(1 - 0.85)); + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public TemaIndicator() : base() + public override string ShortName => $"TEMA {Periods}:{SourceName}"; + + public TemaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "TEMA - Triple Exponential Moving Average"; + Description = "Triple Exponential Moving Average"; + Series = new(name: $"TEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Tema(period: Period); + ma = new Tema(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/TrimaIndicator.cs b/quantower/Averages/TrimaIndicator.cs index b0612356..14e477df 100644 --- a/quantower/Averages/TrimaIndicator.cs +++ b/quantower/Averages/TrimaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class TrimaIndicator : IndicatorBase +public class TrimaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Trima? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"TRIMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"TRIMA {Periods}:{SourceName}"; - public TrimaIndicator() : base() + public TrimaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "TRIMA - Triangular Moving Average"; + Description = "Triangular Moving Average"; + Series = new(name: $"TRIMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Trima(Period); - base.InitIndicator(); + ma = new Trima(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/VidyaIndicator.cs b/quantower/Averages/VidyaIndicator.cs index a9ed10ad..5e411693 100644 --- a/quantower/Averages/VidyaIndicator.cs +++ b/quantower/Averages/VidyaIndicator.cs @@ -1,28 +1,75 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class VidyaIndicator : IndicatorBase +public class VidyaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Short Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; - [InputParameter("Long Period", sortIndex: 2, 1, 2000, 1, 0)] - public int LPeriod { get; set; } = 40; - [InputParameter("Alpha", sortIndex: 3, 0, 1, 0.1, 1)] - public double Alpha { get; set; } = 0.4; + [InputParameter("Short Period", sortIndex: 1, 1, 1000, 1, 0)] + public int ShortPeriod { get; set; } = 14; + + [InputParameter("Long Period", sortIndex: 2, 0, 1000, 1, 0)] + public int LongPeriod { get; set; } = 0; + + [InputParameter("Alpha", sortIndex: 3, 0.01, 1, 0.01, 2)] + public double Alpha { get; set; } = 0.2; + + [InputParameter("Data source", sortIndex: 4, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Vidya? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"VIDYA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => LongPeriod == 0 ? ShortPeriod * 4 : LongPeriod; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"VIDYA {ShortPeriod}:{LongPeriod}:{Alpha}:{SourceName}"; - public VidyaIndicator() : base() + public VidyaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "VIDYA - Variable Index Dynamic Average"; + Description = "Variable Index Dynamic Average"; + Series = new(name: $"VIDYA {ShortPeriod}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Vidya(Period, LPeriod, Alpha); - base.InitIndicator(); + ma = new Vidya(ShortPeriod, LongPeriod, Alpha); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/WmaIndicator.cs b/quantower/Averages/WmaIndicator.cs index 5cc6a396..ba8d8d60 100644 --- a/quantower/Averages/WmaIndicator.cs +++ b/quantower/Averages/WmaIndicator.cs @@ -1,24 +1,69 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class WmaIndicator : IndicatorBase +public class WmaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Wma? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"WMA {Period} : {SourceName}"; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"WMA {Periods}:{SourceName}"; - public WmaIndicator() : base() + public WmaIndicator() { + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); Name = "WMA - Weighted Moving Average"; + Description = "Weighted Moving Average"; + Series = new(name: $"WMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Wma(Period); - base.InitIndicator(); + ma = new Wma(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, Description); } } diff --git a/quantower/Averages/ZlemaIndicator.cs b/quantower/Averages/ZlemaIndicator.cs index c42b045c..a38b9c07 100644 --- a/quantower/Averages/ZlemaIndicator.cs +++ b/quantower/Averages/ZlemaIndicator.cs @@ -1,24 +1,72 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class ZlemaIndicator : IndicatorBase +public class ZlemaIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; private Zlema? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"ZLEMA {Period} : {SourceName}"; + private Huberloss? err; + protected LineSeries? Series; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + public override string ShortName => $"ZLEMA {Periods}:{SourceName}"; - public ZlemaIndicator() : base() + public ZlemaIndicator() { - Name = "ZLEMA - Weighted Moving Average"; + OnBackGround = true; + SeparateWindow = false; + SourceName = Source.ToString(); + Name = "ZLEMA - Zero Lag Exponential Moving Average"; + Description = "Zero Lag Exponential Moving Average"; + Series = new(name: $"ZLEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); } - protected override void InitIndicator() + protected override void OnInit() { - base.InitIndicator(); - ma = new Zlema(Period); + ma = new(Periods); + err = new(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + err!.Calc(input, result); + + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.DrawText(args, err!.Value.ToString()); } } diff --git a/quantower/Averages/Averages.csproj b/quantower/Averages/_Averages.csproj similarity index 70% rename from quantower/Averages/Averages.csproj rename to quantower/Averages/_Averages.csproj index 3fb41cb2..cadffce4 100644 --- a/quantower/Averages/Averages.csproj +++ b/quantower/Averages/_Averages.csproj @@ -1,30 +1,33 @@  + Averages Indicator + 0.0.0.0 bin\$(Configuration)\ true true true + false - - - lib\%(RecursiveDir)%(Filename)%(Extension) - - - - - + + + + - ..\..\.github\TradingPlatform.BusinessLayer.dll + ..\..\.github\TradingPlatform.BusinessLayer.dll TradingPlatform.BusinessLayer.xml - \ No newline at end of file + + + + + diff --git a/quantower/Averages/_IndicatorBase.cs b/quantower/Averages/_IndicatorBase.cs deleted file mode 100644 index 043fd9e1..00000000 --- a/quantower/Averages/_IndicatorBase.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System.Drawing; -using TradingPlatform.BusinessLayer; -using TradingPlatform.BusinessLayer.Chart; -using System.Runtime.CompilerServices; -using System.Drawing.Drawing2D; -using QuanTAlib; -using System.Collections; -using TradingPlatform.BusinessLayer.TimeSync; - -#pragma warning disable CA1416 // Validate platform compatibility -public abstract class IndicatorBase : Indicator, IWatchlistIndicator -{ - - [InputParameter("Data source", sortIndex: 17, variants: [ - "Open", 1, - "High", 2, - "Low", 3, - "Close", 4, - "HL/2 (Median)", 5, - "OC/2 (Midpoint)", 6, - "OHL/3 (Mean)", 7, - "HLC/3 (Typical)", 8, - "OHLC/4 (Average)", 9, - "HLCC/4 (Weighted)", 10 - ])] - public int Source { get; set; } = 4; - - [InputParameter("Show cold values", sortIndex: 20)] - public bool ShowColdValues { get; set; } = true; - public int MinHistoryDepths; - - // LineSeries.LineSeries(string, Color, int, LineStyle)' - - protected LineSeries? Series; - protected string SourceName; - protected abstract AbstractBase QuanTAlib { get; } - - int IWatchlistIndicator.MinHistoryDepths => 0; - - protected IndicatorBase() : base() - { - OnBackGround = true; - SeparateWindow = false; - SourceName = GetName(Source); - Series = new(name: $"{Name}", color: Color.Yellow, width: 2, style: LineStyle.Solid); - - AddLineSeries(Series); - InitIndicator(); - } - - protected virtual void InitIndicator() - { - SourceName = GetName(Source); - } - - protected override void OnInit() - { - InitIndicator(); - base.OnInit(); - } - - protected override void OnUpdate(UpdateArgs args) - { - TBar bar = new(Time: Time(), - Open: GetPrice(PriceType.Open), - High: GetPrice(PriceType.High), - Low: GetPrice(PriceType.Low), - Close: GetPrice(PriceType.Close), - Volume: GetPrice(PriceType.Volume), - IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar); - - double price = Source switch - { - 1 => bar.Open, - 2 => bar.High, - 3 => bar.Low, - 4 => bar.Close, - 5 => bar.HL2, - 6 => bar.OC2, - 7 => bar.OHL3, - 8 => bar.HLC3, - 9 => bar.OHLC4, - 10 => bar.HLCC4, - _ => bar.Close - }; - - TValue input = new TValue(bar.Time, price, bar.IsNew); - TValue result = QuanTAlib.Calc(input); - Series!.SetValue(result.Value); - Series!.SetMarker(0, Color.Transparent); - - } - - public override void OnPaintChart(PaintChartEventArgs args) - { - base.OnPaintChart(args); - List allPoints = new List(); - if (CurrentChart == null) return; - - Graphics gr = args.Graphics; - var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; - var converter = mainWindow.CoordinatesConverter; - var clientRect = mainWindow.ClientRectangle; - - gr.SetClip(clientRect); - DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); - DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); - - int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; - int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); - - for (int i = rightIndex; i < leftIndex; i++) - { - 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); - allPoints.Add(point); - } - - if (allPoints.Count > 1) - { - DrawSmoothCombinedCurve(gr, allPoints, this.Count - QuanTAlib.WarmupPeriod - rightIndex); - } - } - - private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) - { - 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.2); - } - - // 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.2); - } - } - } - private DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) - { - return lineStyle switch - { - LineStyle.Solid => DashStyle.Solid, - LineStyle.Dash => DashStyle.Dash, - LineStyle.Dot => DashStyle.Dot, - LineStyle.DashDot => DashStyle.DashDot, - _ => DashStyle.Solid, - }; - } - protected void DrawText(Graphics gr, string text, Rectangle clientRect) - { - Font font = new Font("Inter", 8); - SizeF textSize = gr.MeasureString(text, font); - RectangleF textRect = new RectangleF(clientRect.Left + 5, - clientRect.Bottom - textSize.Height - 10, - textSize.Width + 10, textSize.Height + 10); - gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); - gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); - } - protected string GetName(int pType) - { - return pType switch - { - 1 => "Open", - 2 => "High", - 3 => "Low", - 4 => "Close", - 5 => "Median", - 6 => "Midpoint", - 7 => "Mean", - 8 => "Typical", - 9 => "Average", - 10 => "Weighted", - _ => "N/A" - }; - } - -} \ No newline at end of file diff --git a/quantower/IndicatorExtensions.cs b/quantower/IndicatorExtensions.cs new file mode 100644 index 00000000..5c9a79dc --- /dev/null +++ b/quantower/IndicatorExtensions.cs @@ -0,0 +1,220 @@ +using TradingPlatform.BusinessLayer; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace QuanTAlib; + +public enum SourceType +{ + Open, High, Low, Close, HL2, OC2, OHL3, HLC3, OHLC4, HLCC4 +} + +public enum MaType +{ + Alma, Dema, Dsma, Dwma, Ema, Epma, Frama, Fwma, Gma, Hma, Hwma, Jma, Kama, Maaf, Mgdi, MMa, Pwma, Rema, Rma, Sinema, Sma, Smma, T3, Tema, Trima, Vidya, Wma, Zlema +} + +public static class IndicatorExtensions +{ + public static TValue GetInputValue(this Indicator indicator, UpdateArgs args, SourceType source) + { + var historicalData = indicator.HistoricalData; + + TBar bar = new TBar( + Time: historicalData.Time(), + Open: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Open], + High: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.High], + Low: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Low], + Close: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Close], + Volume: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Volume], + IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar + ); + + double price = source switch + { + SourceType.Open => bar.Open, + SourceType.High => bar.High, + SourceType.Low => bar.Low, + SourceType.Close => bar.Close, + SourceType.HL2 => bar.HL2, + SourceType.OC2 => bar.OC2, + SourceType.OHL3 => bar.OHL3, + SourceType.HLC3 => bar.HLC3, + SourceType.OHLC4 => bar.OHLC4, + SourceType.HLCC4 => bar.HLCC4, + _ => bar.Close + }; + + return new TValue(bar.Time, price, bar.IsNew); + } + + public static TBar GetInputBar(this Indicator indicator, UpdateArgs args) + { + var historicalData = indicator.HistoricalData; + + return new TBar( + Time: historicalData.Time(), + Open: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Open], + High: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.High], + Low: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Low], + Close: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Close], + Volume: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Volume], + IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar + ); + } + +#pragma warning disable CA1416 // Validate platform compatibility + + public static void PaintHLine(this Indicator indicator, PaintChartEventArgs args, double value, Pen pen) + { + if (indicator.CurrentChart == null) + return; + + Graphics gr = args.Graphics; + var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + gr.SetClip(clientRect); + int leftX = clientRect.Left; + int rightX = clientRect.Right; + int Y = (int)converter.GetChartY(value); + using (pen) + { + gr.DrawLine(pen, new Point(leftX, Y), new Point(rightX, Y)); + } + } + + public static void PaintSmoothCurve(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true, double tension = 0.2) + { + if (!series.Visible || indicator.CurrentChart == null) + return; + + Graphics gr = args.Graphics; + gr.SmoothingMode = SmoothingMode.AntiAlias; + var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), indicator.HistoricalData.Time(indicator!.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), indicator.HistoricalData.Time(0) }.Min(); + + int leftIndex = (int)indicator.HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)indicator.HistoricalData.GetIndexByTime(rightTime.Ticks); + + List allPoints = new List(); + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(indicator.HistoricalData.Time(i)); + int barY = (int)converter.GetChartY(series[i]); + int halfBarWidth = indicator.CurrentChart.BarsWidth / 2; + Point point = new Point(barX + halfBarWidth, barY); + allPoints.Add(point); + } + + if (allPoints.Count > 1) + { + 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 }) + { + int hotCount = indicator.Count - warmupPeriod - rightIndex; + // 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)tension); + } + + // 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)tension); + } + } + } + } + + public static void PaintHistogram(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true) + { + if (!series.Visible || indicator.CurrentChart == null) + return; + + Graphics gr = args.Graphics; + gr.SmoothingMode = SmoothingMode.AntiAlias; + var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), indicator.HistoricalData.Time(indicator!.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), indicator.HistoricalData.Time(0) }.Min(); + int leftIndex = (int)indicator.HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)indicator.HistoricalData.GetIndexByTime(rightTime.Ticks); + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(indicator.HistoricalData.Time(i)); + int barY = (int)converter.GetChartY(series[i]); + int barY0 = (int)converter.GetChartY(0); + int HistBarWidth = indicator.CurrentChart.BarsWidth - 2; + + if (series[i] > 0) + { + using (Brush hist = new SolidBrush(Color.FromArgb(150, 0, 255, 0))) + { + gr.FillRectangle(hist, barX, barY, HistBarWidth, Math.Abs(barY - barY0)); + + } + } + else + { + using (Brush hist = new SolidBrush(Color.FromArgb(150, 255, 0, 0))) + { + gr.FillRectangle(hist, barX, barY0, HistBarWidth, Math.Abs(barY0 - barY)); + + } + } + + } + + } + + + public static void DrawText(this Indicator indicator, PaintChartEventArgs args, string text) + { + if (indicator.CurrentChart == null) + return; + + Graphics gr = args.Graphics; + var clientRect = indicator.CurrentChart.MainWindow.ClientRectangle; + + Font font = new Font("Inter", 8); + SizeF textSize = gr.MeasureString(text, font); + RectangleF textRect = new RectangleF(clientRect.Left + 5, + clientRect.Bottom - textSize.Height - 10, + textSize.Width + 10, textSize.Height + 10); + + gr.FillRectangle(Brushes.DarkBlue, textRect); + gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); + } + + private static DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) + { + return lineStyle switch + { + LineStyle.Solid => DashStyle.Solid, + LineStyle.Dash => DashStyle.Dash, + LineStyle.Dot => DashStyle.Dot, + LineStyle.DashDot => DashStyle.DashDot, + _ => DashStyle.Solid, + }; + } +} + + + + diff --git a/quantower/Statistics/CurvatureIndicator.cs b/quantower/Statistics/CurvatureIndicator.cs new file mode 100644 index 00000000..4ba48c9e --- /dev/null +++ b/quantower/Statistics/CurvatureIndicator.cs @@ -0,0 +1,63 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class CurvatureIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 3, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + private Curvature? curvature; + protected LineSeries? CurvatureSeries; + protected LineSeries? LineSeries; + protected string? SourceName; + public int MinHistoryDepths => Periods * 2 - 1; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public CurvatureIndicator() + { + Name = "Curvature"; + Description = "Calculates the rate of change of the slope over a specified period"; + SeparateWindow = true; + SourceName = Source.ToString(); + + CurvatureSeries = new("Curvature", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(CurvatureSeries); + } + + protected override void OnInit() + { + curvature = new Curvature(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = curvature!.Calc(input); + + CurvatureSeries!.SetValue(result.Value); + if (curvature.Line.HasValue) + { + LineSeries!.SetValue(curvature.Line.Value); + } + } + + public override string ShortName => $"Curvature ({Periods}:{SourceName})"; +} diff --git a/quantower/Statistics/EntropyIndicator.cs b/quantower/Statistics/EntropyIndicator.cs index 8e141c01..5fb2a517 100644 --- a/quantower/Statistics/EntropyIndicator.cs +++ b/quantower/Statistics/EntropyIndicator.cs @@ -1,25 +1,57 @@ +using System.Drawing; using TradingPlatform.BusinessLayer; namespace QuanTAlib; -public class EntropyIndicator : IndicatorBase +public class EntropyIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] - public int Period { get; set; } = 50; + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; private Entropy? entropy; - protected override AbstractBase QuanTAlib => entropy!; - public override string ShortName => $"ENTROPY {Period} : {SourceName}"; + protected LineSeries? EntropySeries; + protected string? SourceName; + public static int MinHistoryDepths => 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public EntropyIndicator() : base() + public EntropyIndicator() { - Name = "ENTROPY - Entropy"; + Name = "Entropy"; + Description = "Measures the unpredictability of data using Shannon's Entropy"; SeparateWindow = true; + SourceName = Source.ToString(); + + EntropySeries = new("Entropy", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(EntropySeries); } - protected override void InitIndicator() + protected override void OnInit() { - entropy = new(Period); - MinHistoryDepths = entropy.WarmupPeriod; - base.InitIndicator(); + entropy = new Entropy(Periods); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = entropy!.Calc(input); + + EntropySeries!.SetValue(result.Value); + } + + public override string ShortName => $"Entropy ({Periods}:{SourceName})"; +} diff --git a/quantower/Statistics/KurtosisIndicator.cs b/quantower/Statistics/KurtosisIndicator.cs index c9d78068..620f53dd 100644 --- a/quantower/Statistics/KurtosisIndicator.cs +++ b/quantower/Statistics/KurtosisIndicator.cs @@ -1,25 +1,58 @@ +using System.Drawing; using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class KurtosisIndicator : IndicatorBase +public class KurtosisIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 4, 2000, 1, 0)] - public int Period { get; set; } = 20; + [InputParameter("Periods", sortIndex: 1, 4, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; private Kurtosis? kurtosis; - protected override AbstractBase QuanTAlib => kurtosis!; - public override string ShortName => $"KURTOSIS {Period} : {SourceName}"; + protected LineSeries? KurtosisSeries; + protected string? SourceName; + public int MinHistoryDepths => Periods - 1; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public KurtosisIndicator() : base() + public KurtosisIndicator() { - Name = "KURTOSIS - Relative Flatness"; + Name = "Kurtosis"; + Description = "Measures the 'tailedness' of the probability distribution of a real-valued random variable"; SeparateWindow = true; + SourceName = Source.ToString(); + + KurtosisSeries = new("Kurtosis", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(KurtosisSeries); } - protected override void InitIndicator() + protected override void OnInit() { - kurtosis = new(Period); - MinHistoryDepths = kurtosis.WarmupPeriod; - base.InitIndicator(); + kurtosis = new Kurtosis(Periods); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = kurtosis!.Calc(input); + + KurtosisSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Kurtosis ({Periods}:{SourceName})"; +} diff --git a/quantower/Statistics/MaxIndicator.cs b/quantower/Statistics/MaxIndicator.cs index e0b6200a..5c696095 100644 --- a/quantower/Statistics/MaxIndicator.cs +++ b/quantower/Statistics/MaxIndicator.cs @@ -1,28 +1,61 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class MaxIndicator : IndicatorBase +public class MaxIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 50; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 20; - [InputParameter("Decay to mean", sortIndex: 1, minimum: 0.00, maximum: 100.0, increment: 0.01, decimalPlaces: 2)] - public double Decay { get; set; } = 0.1; + [InputParameter("Decay", sortIndex: 2, 0, 10, 0.01, 2)] + public double Decay { get; set; } = 0; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.High; private Max? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"MAX {Period} : {Decay:F2} : {SourceName}"; + protected LineSeries? MaxSeries; + protected string? SourceName; + public static int MinHistoryDepths => 0; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public MaxIndicator() : base() + public MaxIndicator() { - Name = "MAX - Maximum value (with decay) "; + Name = "Max"; + Description = "Calculates the maximum value over a specified period, with an optional decay factor"; + SeparateWindow = false; + SourceName = Source.ToString(); + + MaxSeries = new("Max", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(MaxSeries); } - protected override void InitIndicator() + protected override void OnInit() { - ma = new Max(Period, Decay); - MinHistoryDepths = ma.WarmupPeriod; - Source = 2; - base.InitIndicator(); + ma = new Max(Periods, Decay); + SourceName = Source.ToString(); + base.OnInit(); } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + MaxSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Max ({Periods}, {Decay:F2}:{SourceName})"; } diff --git a/quantower/Statistics/MedianIndicator.cs b/quantower/Statistics/MedianIndicator.cs index e2e29d98..471ace45 100644 --- a/quantower/Statistics/MedianIndicator.cs +++ b/quantower/Statistics/MedianIndicator.cs @@ -1,23 +1,58 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class MedianIndicator : IndicatorBase +public class MedianIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 50; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; private Median? med; - protected override AbstractBase QuanTAlib => med!; - public override string ShortName => $"MEDIAN {Period} : {SourceName}"; - public MedianIndicator() : base() + protected LineSeries? MedianSeries; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public MedianIndicator() { - Name = "MEDIAN - Median historical value"; + Name = "Median"; + Description = "Calculates the median value over a specified period"; + SeparateWindow = false; + SourceName = Source.ToString(); + + MedianSeries = new("Median", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(MedianSeries); } - protected override void InitIndicator() + protected override void OnInit() { - med = new Median(Period); - MinHistoryDepths = med.WarmupPeriod; - base.InitIndicator(); + med = new Median(Periods); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = med!.Calc(input); + + MedianSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Median ({Periods}:{SourceName})"; +} diff --git a/quantower/Statistics/MinIndicator.cs b/quantower/Statistics/MinIndicator.cs index a4c14e9e..7f55fcd4 100644 --- a/quantower/Statistics/MinIndicator.cs +++ b/quantower/Statistics/MinIndicator.cs @@ -1,27 +1,61 @@ -using TradingPlatform.BusinessLayer; +using System.Drawing; +using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class MinIndicator : IndicatorBase +public class MinIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 50; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 20; - [InputParameter("Decay to mean", sortIndex: 1, minimum: 0.00, maximum: 100.0, increment: 0.01, decimalPlaces: 2)] - public double Decay { get; set; } = 0.1; + [InputParameter("Decay", sortIndex: 2, 0, 10, 0.01, 2)] + public double Decay { get; set; } = 0; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Low; private Min? mi; - protected override AbstractBase QuanTAlib => mi!; - public override string ShortName => $"MIN {Period} : {Decay:F2} : {SourceName}"; - public MinIndicator() : base() + protected LineSeries? MinSeries; + protected string? SourceName; + public static int MinHistoryDepths => 0; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public MinIndicator() { - Name = "MIN - Minimum value (with decay)"; + Name = "Min"; + Description = "Calculates the minimum value over a specified period, with an optional decay factor"; + SeparateWindow = false; + SourceName = Source.ToString(); + + MinSeries = new("Min", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(MinSeries); } - protected override void InitIndicator() + protected override void OnInit() { - mi = new Min(Period, Decay); - MinHistoryDepths = mi.WarmupPeriod; - Source = 3; - base.InitIndicator(); + mi = new Min(Periods, Decay); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = mi!.Calc(input); + + MinSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Min ({Periods}, {Decay:F2}:{SourceName})"; +} diff --git a/quantower/Statistics/ModeIndicator.cs b/quantower/Statistics/ModeIndicator.cs index c293b643..7294ed2c 100644 --- a/quantower/Statistics/ModeIndicator.cs +++ b/quantower/Statistics/ModeIndicator.cs @@ -1,23 +1,58 @@ +using System.Drawing; using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class ModeIndicator : IndicatorBase +public class ModeIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 50; + [InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; private Mode? mode; - protected override AbstractBase QuanTAlib => mode!; - public override string ShortName => $"MODE {Period} : {SourceName}"; - public ModeIndicator() : base() + protected LineSeries? ModeSeries; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public ModeIndicator() { - Name = "MODE - Most frequent historical value"; + Name = "Mode"; + Description = "Calculates the most frequent value in a specified period"; + SeparateWindow = false; + SourceName = Source.ToString(); + + ModeSeries = new("Mode", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(ModeSeries); } - protected override void InitIndicator() + protected override void OnInit() { - mode = new Mode(Period); - MinHistoryDepths = mode.WarmupPeriod; - base.InitIndicator(); + mode = new Mode(Periods); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = mode!.Calc(input); + + ModeSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Mode ({Periods}:{SourceName})"; +} diff --git a/quantower/Statistics/PercentileIndicator.cs b/quantower/Statistics/PercentileIndicator.cs index dacb2fd9..a6cc5a2d 100644 --- a/quantower/Statistics/PercentileIndicator.cs +++ b/quantower/Statistics/PercentileIndicator.cs @@ -1,28 +1,61 @@ +using System.Drawing; using TradingPlatform.BusinessLayer; -namespace QuanTAlib; -public class PercentileIndicator : IndicatorBase -{ - [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] - public int Period { get; set; } = 20; - [InputParameter("Percent", sortIndex: 2, 0, 100, 1, 0)] - public double Percent { get; set; } = 50; +namespace QuanTAlib; + +public class PercentileIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Percentile", sortIndex: 2, 0, 100, 0.1, 1)] + public double PercentileValue { get; set; } = 50; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; private Percentile? percentile; - protected override AbstractBase QuanTAlib => percentile!; - public override string ShortName => $"PERCENTILE {Period} {Percent:F0}% : {SourceName}"; + protected LineSeries? PercentileSeries; + protected string? SourceName; + public static int MinHistoryDepths => 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public PercentileIndicator() : base() + public PercentileIndicator() { - Name = "PERCENTILE - n-th Percentile "; + Name = "Percentile"; + Description = "Calculates the value at a specified percentile in a given period of data points"; SeparateWindow = false; + SourceName = Source.ToString(); + + PercentileSeries = new("Percentile", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(PercentileSeries); } - protected override void InitIndicator() + protected override void OnInit() { - percentile = new(Period, Percent); - MinHistoryDepths = percentile.WarmupPeriod; - base.InitIndicator(); + percentile = new Percentile(Periods, PercentileValue); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = percentile!.Calc(input); + + PercentileSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Percentile ({Periods}, {PercentileValue}%:{SourceName})"; +} diff --git a/quantower/Statistics/SkewIndicator.cs b/quantower/Statistics/SkewIndicator.cs index 7cce283e..0430c064 100644 --- a/quantower/Statistics/SkewIndicator.cs +++ b/quantower/Statistics/SkewIndicator.cs @@ -1,26 +1,58 @@ - +using System.Drawing; using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class SkewIndicator : IndicatorBase +public class SkewIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 3, 2000, 1, 0)] - public int Period { get; set; } = 20; + [InputParameter("Periods", sortIndex: 1, 3, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; private Skew? skew; - protected override AbstractBase QuanTAlib => skew!; - public override string ShortName => $"SKEW {Period} : {SourceName}"; + protected LineSeries? SkewSeries; + protected string? SourceName; + public static int MinHistoryDepths => 3; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public SkewIndicator() : base() + public SkewIndicator() { - Name = "SKEW - Skewness"; + Name = "Skew"; + Description = "Measures the asymmetry of the probability distribution of a real-valued random variable about its mean"; SeparateWindow = true; + SourceName = Source.ToString(); + + SkewSeries = new("Skew", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(SkewSeries); } - protected override void InitIndicator() + protected override void OnInit() { - skew = new(Period); - MinHistoryDepths = skew.WarmupPeriod; - base.InitIndicator(); + skew = new Skew(Periods); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = skew!.Calc(input); + + SkewSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Skew ({Periods}:{SourceName})"; +} diff --git a/quantower/Statistics/SlopeIndicator.cs b/quantower/Statistics/SlopeIndicator.cs new file mode 100644 index 00000000..c5d32741 --- /dev/null +++ b/quantower/Statistics/SlopeIndicator.cs @@ -0,0 +1,82 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class SlopeIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + private Slope? slope; + protected LineSeries? SlopeSeries; + protected LineSeries? LineSeries; + protected string? SourceName; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public SlopeIndicator() + { + Name = "Slope"; + Description = "Calculates the slope of a linear regression line for the specified period"; + SeparateWindow = true; + SourceName = Source.ToString(); + + SlopeSeries = new("Slope", Color.Blue, 2, LineStyle.Solid); + LineSeries = new("Regression Line", Color.Red, 1, LineStyle.Solid); + AddLineSeries(SlopeSeries); + AddLineSeries(LineSeries); + } + + protected override void OnInit() + { + slope = new Slope(Periods); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = slope!.Calc(input); + + SlopeSeries!.SetValue(result.Value); + if (slope.Line.HasValue) + { + LineSeries!.SetValue(slope.Line.Value); + } + } + + public override string ShortName + { + get + { + var result = $"Slope ({Periods}:{SourceName})"; + if (slope != null) + { + result += $" Slope: {Math.Round(SlopeSeries!.GetValue(), 6)}"; + if (slope.Line.HasValue) + result += $", Line: {Math.Round(slope.Line.Value, 6)}"; + if (slope.Intercept.HasValue) + result += $", Intercept: {Math.Round(slope.Intercept.Value, 6)}"; + if (slope.RSquared.HasValue) + result += $", R²: {Math.Round(slope.RSquared.Value, 6)}"; + } + return result; + } + } +} diff --git a/quantower/Statistics/StddevIndicator.cs b/quantower/Statistics/StddevIndicator.cs index 6c9648c2..a9139d91 100644 --- a/quantower/Statistics/StddevIndicator.cs +++ b/quantower/Statistics/StddevIndicator.cs @@ -1,27 +1,61 @@ +using System.Drawing; using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class StddevIndicator : IndicatorBase +public class StddevIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 20; + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 20; [InputParameter("Population", sortIndex: 2)] public bool IsPopulation { get; set; } = false; + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + private Stddev? stddev; - protected override AbstractBase QuanTAlib => stddev!; - public override string ShortName => $"STDDEV {Period} : {SourceName}"; - public StddevIndicator() : base() + protected LineSeries? StddevSeries; + protected string? SourceName; + public static int MinHistoryDepths => 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public StddevIndicator() { - Name = "STDDEV - Standard Deviation"; + Name = "Standard Deviation"; + Description = "Measures the amount of variation or dispersion of a set of values"; SeparateWindow = true; + SourceName = Source.ToString(); + + StddevSeries = new("StdDev", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(StddevSeries); } - protected override void InitIndicator() + protected override void OnInit() { - stddev = new(Period, IsPopulation); - MinHistoryDepths = stddev.WarmupPeriod; - base.InitIndicator(); + stddev = new Stddev(Periods, IsPopulation); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = stddev!.Calc(input); + + StddevSeries!.SetValue(result.Value); + } + + public override string ShortName => $"StdDev ({Periods}, {(IsPopulation ? "Pop" : "Sample")}:{SourceName})"; +} diff --git a/quantower/Statistics/VarianceIndicator.cs b/quantower/Statistics/VarianceIndicator.cs new file mode 100644 index 00000000..e2c6ca2e --- /dev/null +++ b/quantower/Statistics/VarianceIndicator.cs @@ -0,0 +1,61 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class VarianceIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 2, 1000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Population", sortIndex: 2)] + public bool IsPopulation { get; set; } = false; + + [InputParameter("Data source", sortIndex: 3, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + private Variance? variance; + protected LineSeries? VarianceSeries; + protected string? SourceName; + public static int MinHistoryDepths => 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public VarianceIndicator() + { + Name = "Variance"; + Description = "Measures the spread of a set of numbers from their average value"; + SeparateWindow = true; + SourceName = Source.ToString(); + + VarianceSeries = new("Variance", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(VarianceSeries); + } + + protected override void OnInit() + { + variance = new Variance(Periods, IsPopulation); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = variance!.Calc(input); + + VarianceSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Variance ({Periods}, {(IsPopulation ? "Pop" : "Sample")}:{SourceName})"; +} diff --git a/quantower/Statistics/VarianceIndictor.cs b/quantower/Statistics/VarianceIndictor.cs deleted file mode 100644 index de983873..00000000 --- a/quantower/Statistics/VarianceIndictor.cs +++ /dev/null @@ -1,28 +0,0 @@ -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class VarianceIndicator : IndicatorBase -{ - [InputParameter("Period", sortIndex: 1, minimum: 2, maximum: 2000, increment: 1, decimalPlaces: 0)] - public int Period { get; set; } = 20; - - [InputParameter("Population", sortIndex: 2)] - public bool IsPopulation { get; set; } = false; - - private Variance? variance; - protected override AbstractBase QuanTAlib => variance!; - public override string ShortName => $"VAR {Period} : {SourceName}"; - public VarianceIndicator() : base() - { - Name = "VAR - Variance"; - SeparateWindow = true; - } - - protected override void InitIndicator() - { - SeparateWindow = true; - variance = new(Period, IsPopulation); - MinHistoryDepths = variance.WarmupPeriod; - base.InitIndicator(); - } -} \ No newline at end of file diff --git a/quantower/Statistics/ZscoreIndicator.cs b/quantower/Statistics/ZscoreIndicator.cs index 19727511..e2a52ead 100644 --- a/quantower/Statistics/ZscoreIndicator.cs +++ b/quantower/Statistics/ZscoreIndicator.cs @@ -1,26 +1,58 @@ +using System.Drawing; using TradingPlatform.BusinessLayer; + namespace QuanTAlib; -public class ZScoreIndicator : IndicatorBase +public class ZscoreIndicator : Indicator, IWatchlistIndicator { - [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] - public int Period { get; set; } = 20; + [InputParameter("Periods", sortIndex: 1, 2, 2000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Data source", sortIndex: 2, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; private Zscore? zScore; - protected override AbstractBase QuanTAlib => zScore!; - public override string ShortName => $"ZSCORE {Period} : {SourceName}"; + protected LineSeries? ZscoreSeries; + protected string? SourceName; + public static int MinHistoryDepths => 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; - public ZScoreIndicator() : base() + public ZscoreIndicator() { - Name = "ZSCORE - Standard Score"; + Name = "Z-Score"; + Description = "Measures how many standard deviations a price is from the mean, indicating overbought/oversold levels."; SeparateWindow = true; + SourceName = Source.ToString(); + + ZscoreSeries = new("Z-Score", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(ZscoreSeries); } - protected override void InitIndicator() + protected override void OnInit() { - zScore = new(Period); - MinHistoryDepths = zScore.WarmupPeriod; - base.InitIndicator(); + zScore = new Zscore(Periods); + SourceName = Source.ToString(); + base.OnInit(); } -} \ No newline at end of file + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = zScore!.Calc(input); + + ZscoreSeries!.SetValue(result.Value); + } + + public override string ShortName => $"Z-Score ({Periods}:{SourceName})"; +} diff --git a/quantower/Statistics/_IndicatorBase.cs b/quantower/Statistics/_IndicatorBase.cs deleted file mode 100644 index aef60342..00000000 --- a/quantower/Statistics/_IndicatorBase.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System.Drawing; -using TradingPlatform.BusinessLayer; -using TradingPlatform.BusinessLayer.Chart; -using System.Runtime.CompilerServices; -using System.Drawing.Drawing2D; -using System.Collections; -using TradingPlatform.BusinessLayer.TimeSync; - -namespace QuanTAlib; - -#pragma warning disable CA1416 // Validate platform compatibility -public abstract class IndicatorBase : Indicator, IWatchlistIndicator -{ - - [InputParameter("Data source", sortIndex: 17, variants: [ - "Open", 1, - "High", 2, - "Low", 3, - "Close", 4, - "HL/2 (Median)", 5, - "OC/2 (Midpoint)", 6, - "OHL/3 (Mean)", 7, - "HLC/3 (Typical)", 8, - "OHLC/4 (Average)", 9, - "HLCC/4 (Weighted)", 10 - ])] - public int Source { get; set; } = 4; - - [InputParameter("Show cold values", sortIndex: 20)] - public bool ShowColdValues { get; set; } = true; - public int MinHistoryDepths; - - // LineSeries.LineSeries(string, Color, int, LineStyle)' - - protected LineSeries? Series; - protected string SourceName; - protected abstract AbstractBase QuanTAlib { get; } - - int IWatchlistIndicator.MinHistoryDepths => 0; - - protected IndicatorBase() : base() - { - OnBackGround = true; - SeparateWindow = false; - SourceName = GetName(Source); - Series = new(name: $"{Name}", color: Color.RoyalBlue, width: 2, style: LineStyle.Solid); - - AddLineSeries(Series); - InitIndicator(); - } - - protected virtual void InitIndicator() - { - SourceName = GetName(Source); - } - - protected override void OnInit() - { - InitIndicator(); - base.OnInit(); - } - - protected override void OnUpdate(UpdateArgs args) - { - TBar bar = new(Time: Time(), - Open: GetPrice(PriceType.Open), - High: GetPrice(PriceType.High), - Low: GetPrice(PriceType.Low), - Close: GetPrice(PriceType.Close), - Volume: GetPrice(PriceType.Volume), - IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar); - - double price = Source switch - { - 1 => bar.Open, - 2 => bar.High, - 3 => bar.Low, - 4 => bar.Close, - 5 => bar.HL2, - 6 => bar.OC2, - 7 => bar.OHL3, - 8 => bar.HLC3, - 9 => bar.OHLC4, - 10 => bar.HLCC4, - _ => bar.Close - }; - - TValue input = new TValue(bar.Time, price, bar.IsNew); - TValue result = QuanTAlib.Calc(input); - Series!.SetValue(result.Value); - Series!.SetMarker(0, Color.Transparent); - - } - - public override void OnPaintChart(PaintChartEventArgs args) - { - base.OnPaintChart(args); - List allPoints = new List(); - if (CurrentChart == null) return; - - Graphics gr = args.Graphics; - - var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; - var converter = mainWindow.CoordinatesConverter; - var clientRect = mainWindow.ClientRectangle; - - gr.SetClip(clientRect); - DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); - DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); - - int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; - int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); - - for (int i = rightIndex; i < leftIndex; i++) - { - 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); - allPoints.Add(point); - } - - if (allPoints.Count > 1) - { - DrawSmoothCombinedCurve(gr, allPoints, this.Count - QuanTAlib.WarmupPeriod - rightIndex); - } - } - - private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) - { - 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); - } - - // 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 DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) - { - return lineStyle switch - { - LineStyle.Solid => DashStyle.Solid, - LineStyle.Dash => DashStyle.Dash, - LineStyle.Dot => DashStyle.Dot, - LineStyle.DashDot => DashStyle.DashDot, - _ => DashStyle.Solid, - }; - } - protected void DrawText(Graphics gr, string text, Rectangle clientRect) - { - Font font = new Font("Inter", 8); - SizeF textSize = gr.MeasureString(text, font); - RectangleF textRect = new RectangleF(clientRect.Left + 5, - clientRect.Bottom - textSize.Height - 10, - textSize.Width + 10, textSize.Height + 10); - gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); - gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); - } - protected string GetName(int pType) - { - return pType switch - { - 1 => "Open", - 2 => "High", - 3 => "Low", - 4 => "Close", - 5 => "Median", - 6 => "Midpoint", - 7 => "Mean", - 8 => "Typical", - 9 => "Average", - 10 => "Weighted", - _ => "N/A" - }; - } - -} \ No newline at end of file diff --git a/quantower/Statistics/Statistics.csproj b/quantower/Statistics/_Statistics.csproj similarity index 63% rename from quantower/Statistics/Statistics.csproj rename to quantower/Statistics/_Statistics.csproj index 36fadba0..3f3ccd37 100644 --- a/quantower/Statistics/Statistics.csproj +++ b/quantower/Statistics/_Statistics.csproj @@ -1,30 +1,32 @@  + Statistics Indicator + 0.0.0.0 bin\$(Configuration)\ true true true + false - - - lib\%(RecursiveDir)%(Filename)%(Extension) - - - - - + + + - ..\..\.github\TradingPlatform.BusinessLayer.dll + ..\..\.github\TradingPlatform.BusinessLayer.dll TradingPlatform.BusinessLayer.xml - \ No newline at end of file + + + + + diff --git a/quantower/Volatility/AtrIndicator.cs b/quantower/Volatility/AtrIndicator.cs new file mode 100644 index 00000000..72c95e9c --- /dev/null +++ b/quantower/Volatility/AtrIndicator.cs @@ -0,0 +1,54 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class AtrIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Atr? atr; + protected LineSeries? AtrSeries; + public int MinHistoryDepths => Math.Max(5, Periods * 2); + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public AtrIndicator() + { + Name = "ATR - Average True Range"; + Description = "Measures market volatility by calculating the average range between high and low prices."; + SeparateWindow = true; + + AtrSeries = new($"ATR {Periods}", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(AtrSeries); + } + + protected override void OnInit() + { + atr = new Atr(Periods); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar input = IndicatorExtensions.GetInputBar(this, args); + TValue result = atr!.Calc(input); + + AtrSeries!.SetValue(result.Value); + AtrSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + + } +#pragma warning disable CA1416 // Validate platform compatibility + + public override string ShortName => $"ATR ({Periods})"; + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintHLine(args, 0.05, new Pen(Color.DarkRed, width: 2)); + this.PaintSmoothCurve(args, AtrSeries!, atr!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + } +} diff --git a/quantower/Volatility/CmoIndicator.cs b/quantower/Volatility/CmoIndicator.cs new file mode 100644 index 00000000..6a7a069d --- /dev/null +++ b/quantower/Volatility/CmoIndicator.cs @@ -0,0 +1,72 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class CmoIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)] + public int Periods { get; set; } = 9; + + [InputParameter("Data source", sortIndex: 5, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Cmo? cmo; + protected string? SourceName; + protected LineSeries? CmoSeries; + public int MinHistoryDepths => Periods + 1; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + + public CmoIndicator() + { + Name = "CMO - Chande Momentum Oscillator"; + Description = "Measures the momentum of price changes using the difference between the sum of recent gains and the sum of recent losses."; + SeparateWindow = true; + SourceName = Source.ToString(); + CmoSeries = new($"CMO {Periods}", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(CmoSeries); + } + + protected override void OnInit() + { + cmo = new Cmo(Periods); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + cmo!.Calc(input); + + CmoSeries!.SetValue(cmo.Value); + CmoSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + + } + + public override string ShortName => $"CMO ({Periods}:{SourceName})"; + +#pragma warning disable CA1416 // Validate platform compatibility + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintHLine(args, 0, new Pen(Color.DarkGray, width: 1)); + this.PaintHLine(args, 50, new Pen(Color.Blue, width: 1)); + this.PaintHLine(args, -50, new Pen(Color.Blue, width: 1)); + this.PaintSmoothCurve(args, CmoSeries!, cmo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + } +} diff --git a/quantower/Volatility/FlowIndicator.cs b/quantower/Volatility/FlowIndicator.cs new file mode 100644 index 00000000..07dc9c2d --- /dev/null +++ b/quantower/Volatility/FlowIndicator.cs @@ -0,0 +1,86 @@ +using System.Drawing; +using System.Drawing.Drawing2D; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class FlowIndicator : Indicator, IWatchlistIndicator +{ + protected string? SourceName; + public static int MinHistoryDepths => 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public FlowIndicator() + { + Name = "Flow Visualization"; + SeparateWindow = false; + } + + protected override void OnInit() + { + // placeholder + } + + protected override void OnUpdate(UpdateArgs args) + { + // placeholder + } + +#pragma warning disable CA1416 // Validate platform compatibility + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + Graphics gr = args.Graphics; + gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; + var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), this.HistoricalData.Time(this!.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), this.HistoricalData.Time(0) }.Min(); + + int leftIndex = (int)this.HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)this.HistoricalData.GetIndexByTime(rightTime.Ticks); + int width = this.CurrentChart.BarsWidth; + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX1 = (int)converter.GetChartX(this.HistoricalData.Time(i)); + int barY1 = (int)converter.GetChartY(this.HistoricalData.Open(i)); + int barYHigh = (int)converter.GetChartY(this.HistoricalData.High(i)); + int barYLow = (int)converter.GetChartY(this.HistoricalData.Low(i)); + int barX2 = barX1 + width; + int barY2 = (int)converter.GetChartY(this.HistoricalData.Close(i)); + using (Brush transparentBrush = new SolidBrush(Color.FromArgb(250, 70, 70, 70))) + { + gr.FillRectangle(transparentBrush, barX1, barYHigh - 1, CurrentChart.BarsWidth, Math.Abs(barYLow - barYHigh) + 2); + } + using (Brush circ = new SolidBrush(Color.FromArgb(100, 255, 255, 0))) + { + int size = 3; + gr.FillEllipse(circ, barX1 - size, barY1 - size, 2 * size, 2 * size); + gr.FillEllipse(circ, barX2 - size, barY2 - size, 2 * size, 2 * size); + } + using (Pen defaultPen = new(Color.Yellow, 3)) + { + defaultPen.StartCap = LineCap.Round; + defaultPen.EndCap = LineCap.Round; + gr.DrawLine(defaultPen, barX1, barY1, barX2, barY2); + } + if (i > 0) + { + int barX0 = (int)converter.GetChartX(this.HistoricalData.Time(i - 1)); + int barY0 = (int)converter.GetChartY(this.HistoricalData.Open(i - 1)); + using (Pen dottedPen = new(Color.Yellow, 1)) + { + dottedPen.DashStyle = DashStyle.Dot; + gr.DrawLine(dottedPen, barX2, barY2, barX0, barY0); + } + } + + + } + } + +} diff --git a/quantower/Volatility/HistoricalIndicator.cs b/quantower/Volatility/HistoricalIndicator.cs new file mode 100644 index 00000000..06ed5ec8 --- /dev/null +++ b/quantower/Volatility/HistoricalIndicator.cs @@ -0,0 +1,44 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class HistoricalIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Annualized", sortIndex: 2)] + public bool IsAnnualized { get; set; } = true; + + private Historical? historical; + protected LineSeries? HvSeries; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public HistoricalIndicator() + { + Name = "HV - Historical Volatility"; + Description = "Measures price fluctuations over time, indicating market volatility based on past price movements."; + SeparateWindow = true; + + HvSeries = new("HV", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(HvSeries); + } + + protected override void OnInit() + { + historical = new Historical(Periods, IsAnnualized); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar input = IndicatorExtensions.GetInputBar(this, args); + TValue result = historical!.Calc(input); + + HvSeries!.SetValue(result.Value); + } + + public override string ShortName => $"HV ({Periods}{(IsAnnualized ? " - Annualized" : "")})"; +} diff --git a/quantower/Volatility/JbandsIndicator.cs b/quantower/Volatility/JbandsIndicator.cs new file mode 100644 index 00000000..a77312c8 --- /dev/null +++ b/quantower/Volatility/JbandsIndicator.cs @@ -0,0 +1,67 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class JbandsIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 5, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("vShort", sortIndex: 6, -100, 100, 1, 0)] + public int Phase { get; set; } = 10; + + private Jma? jmaUp; + private Jma? jmaLo; + protected LineSeries? UbSeries; + protected LineSeries? LbSeries; + protected string? SourceName; + public static int MinHistoryDepths => 2; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public JbandsIndicator() + { + Name = "JBANDS - Mark Jurik's Bands"; + Description = "Upper and Lower Bands."; + SeparateWindow = false; + + UbSeries = new("UB", Color.Blue, 2, LineStyle.Solid); + LbSeries = new("LB", Color.Red, 2, LineStyle.Solid); + AddLineSeries(UbSeries); + AddLineSeries(LbSeries); + } + + protected override void OnInit() + { + jmaUp = new(Periods, phase: Phase); + jmaLo = new(Periods, phase: Phase); + SourceName = Source.ToString(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar input = IndicatorExtensions.GetInputBar(this, args); + jmaUp!.Calc(input.High); + jmaLo!.Calc(input.Low); + + UbSeries!.SetValue(jmaUp.UpperBand); + LbSeries!.SetValue(jmaLo.LowerBand); + } + + public override string ShortName => $"JBands ({Periods}:{Phase})"; +} diff --git a/quantower/Volatility/JvoltyIndicator.cs b/quantower/Volatility/JvoltyIndicator.cs new file mode 100644 index 00000000..7a6436b5 --- /dev/null +++ b/quantower/Volatility/JvoltyIndicator.cs @@ -0,0 +1,58 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class JvoltyIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 5, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + private Jma? jma; + protected LineSeries? JvoltySeries; + public static int MinHistoryDepths => 2; + + + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public JvoltyIndicator() + { + Name = "JVOLTY - Mark Jurik's Volatility"; + Description = "Measures market volatility according to Mark Jurik."; + SeparateWindow = true; + + JvoltySeries = new("JVOLTY", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(JvoltySeries); + } + + protected override void OnInit() + { + jma = new(Periods); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + jma!.Calc(input); + + JvoltySeries!.SetValue(jma.Volty); + + } + + public override string ShortName => $"JVOLTY ({Periods})"; +} diff --git a/quantower/Volatility/RealizedIndicator.cs b/quantower/Volatility/RealizedIndicator.cs new file mode 100644 index 00000000..126658c7 --- /dev/null +++ b/quantower/Volatility/RealizedIndicator.cs @@ -0,0 +1,44 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class RealizedIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)] + public int Periods { get; set; } = 20; + + [InputParameter("Annualized", sortIndex: 2)] + public bool IsAnnualized { get; set; } = true; + + private Realized? realized; + protected LineSeries? RvSeries; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public RealizedIndicator() + { + Name = "RV - Realized Volatility"; + Description = "Measures actual price volatility over a specific period, useful for risk assessment and forecasting."; + SeparateWindow = true; + + RvSeries = new("RV", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(RvSeries); + } + + protected override void OnInit() + { + realized = new Realized(Periods, IsAnnualized); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar input = IndicatorExtensions.GetInputBar(this, args); + TValue result = realized!.Calc(input); + + RvSeries!.SetValue(result.Value); + } + + public override string ShortName => $"RV ({Periods}{(IsAnnualized ? " - Annualized" : "")})"; +} diff --git a/quantower/Volatility/RsiIndicator.cs b/quantower/Volatility/RsiIndicator.cs new file mode 100644 index 00000000..fd0a4eec --- /dev/null +++ b/quantower/Volatility/RsiIndicator.cs @@ -0,0 +1,67 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class RsiIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)] + public int Periods { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 5, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Rsi? rsi; + protected string? SourceName; + protected LineSeries? RsiSeries; + public int MinHistoryDepths => Periods + 1; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public RsiIndicator() + { + Name = "RSI - Relative Strength Index"; + Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions."; + SeparateWindow = true; + SourceName = Source.ToString(); + RsiSeries = new($"RSI {Periods}", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(RsiSeries); + } + + protected override void OnInit() + { + rsi = new Rsi(Periods); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + rsi!.Calc(input); + + RsiSeries!.SetValue(rsi.Value); + RsiSeries!.SetMarker(0, Color.Transparent); + } + + public override string ShortName => $"RSI ({Periods}:{SourceName})"; + +#pragma warning disable CA1416 // Validate platform compatibility + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, RsiSeries!, rsi!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + } +} diff --git a/quantower/Volatility/RsxIndicator.cs b/quantower/Volatility/RsxIndicator.cs new file mode 100644 index 00000000..5158bc77 --- /dev/null +++ b/quantower/Volatility/RsxIndicator.cs @@ -0,0 +1,67 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class RsxIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Rsi Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 14; + + [InputParameter("Data source", sortIndex: 5, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Rsx? rsx; + protected string? SourceName; + protected LineSeries? RsxSeries; + public int MinHistoryDepths => Period + 1; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public RsxIndicator() + { + Name = "RSX - Jurik Trend Strengt Index"; + Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions."; + SeparateWindow = true; + SourceName = Source.ToString(); + RsxSeries = new($"RSX {Period}", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(RsxSeries); + } + + protected override void OnInit() + { + rsx = new(Period); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + rsx!.Calc(input); + + RsxSeries!.SetValue(rsx.Value); + RsxSeries!.SetMarker(0, Color.Transparent); + } + + public override string ShortName => $"RSX ({Period}:{SourceName})"; + +#pragma warning disable CA1416 // Validate platform compatibility + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, RsxSeries!, rsx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + } +} diff --git a/quantower/Volatility/RviIndicator.cs b/quantower/Volatility/RviIndicator.cs new file mode 100644 index 00000000..005ae679 --- /dev/null +++ b/quantower/Volatility/RviIndicator.cs @@ -0,0 +1,41 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class RviIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Periods", sortIndex: 1, 2, 100, 1, 0)] + public int Periods { get; set; } = 10; + + private Rvi? rvi; + protected LineSeries? RviSeries; + public int MinHistoryDepths => Periods; + int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; + + public RviIndicator() + { + Name = "RVI - Relative Volatility Index"; + Description = "Measures the direction of volatility, helping to identify overbought or oversold conditions in price."; + SeparateWindow = true; + + RviSeries = new("RVI", Color.Blue, 2, LineStyle.Solid); + AddLineSeries(RviSeries); + } + + protected override void OnInit() + { + rvi = new Rvi(Periods); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar input = IndicatorExtensions.GetInputBar(this, args); + TValue result = rvi!.Calc(input); + + RviSeries!.SetValue(result.Value); + } + + public override string ShortName => $"RVI ({Periods})"; +} diff --git a/quantower/Volatility/TestIndicator.cs b/quantower/Volatility/TestIndicator.cs new file mode 100644 index 00000000..fbb60c9a --- /dev/null +++ b/quantower/Volatility/TestIndicator.cs @@ -0,0 +1,65 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib; + +public class TestIndicator : Indicator, IWatchlistIndicator +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Data source", sortIndex: 20, variants: [ + "Open", SourceType.Open, + "High", SourceType.High, + "Low", SourceType.Low, + "Close", SourceType.Close, + "HL/2 (Median)", SourceType.HL2, + "OC/2 (Midpoint)", SourceType.OC2, + "OHL/3 (Mean)", SourceType.OHL3, + "HLC/3 (Typical)", SourceType.HLC3, + "OHLC/4 (Average)", SourceType.OHLC4, + "HLCC/4 (Weighted)", SourceType.HLCC4 + ])] + public SourceType Source { get; set; } = SourceType.Close; + + [InputParameter("Show cold values", sortIndex: 21)] + public bool ShowColdValues { get; set; } = true; + + private Sma? ma; + protected LineSeries? Series; + public int MinHistoryDepths { get; set; } + int IWatchlistIndicator.MinHistoryDepths => 0; //QuanTAlib indicators generate value immediately + + + public TestIndicator() + { + OnBackGround = true; + SeparateWindow = false; + Name = "TEST"; + Description = "test and test and test and more test."; + Series = new(name: $"{Name}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + AddLineSeries(Series); + } + + protected override void OnInit() + { + ma = new Sma(Period); + base.OnInit(); + } + protected override void OnUpdate(UpdateArgs args) + { + TValue input = this.GetInputValue(args, Source); + TValue result = ma!.Calc(input); + + Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here + Series!.SetValue(result); + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, ShowColdValues, tension: 0.2); + this.DrawText(args, Description); + } +} + diff --git a/quantower/Volatility/_Volatility.csproj b/quantower/Volatility/_Volatility.csproj new file mode 100644 index 00000000..eabfa6b9 --- /dev/null +++ b/quantower/Volatility/_Volatility.csproj @@ -0,0 +1,33 @@ + + + Volatility + Indicator + 0.0.0.0 + bin\$(Configuration)\ + true + true + true + false + + + + + + + + + + + + ..\..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + + + + + + + +