diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 00000000..f3badbc4 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,20 @@ +version = 1 + +[[analyzers]] +name = "csharp" +enabled = true + +[analyzers.meta] +language_version = "11.0" + +[[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/workflows/Publish.yml b/.github/workflows/Publish.yml new file mode 100644 index 00000000..68ad111e --- /dev/null +++ b/.github/workflows/Publish.yml @@ -0,0 +1,306 @@ +# This workflow integrates SonarCloud analysis, coverage reporting, +# CodeQL analysis, SecurityCodeScan, and Codacy Security Scan +# for code scanning and vulnerability detection. + +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: + SonarCloud: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.x' + + - name: Install JDK11 for Sonar Scanner + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'zulu' + + - name: Install dotnet-sonarscanner + run: | + dotnet tool install --global dotnet-sonarscanner + dotnet tool install JetBrains.dotCover.GlobalTool --global + dotnet tool install dotnet-coverage --global + dotnet restore + + - name: SonarCloud Scanner Start + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + dotnet sonarscanner begin \ + /k:"mihakralj_QuanTAlib" \ + /o:"mihakralj" \ + /d:sonar.login="${{ secrets.SONAR_TOKEN }}" \ + /d:sonar.host.url="https://sonarcloud.io" \ + /d:sonar.cs.dotcover.reportsPaths=dotcover* + + - name: Build + 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 + dotnet dotcover test Tests/Tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html + + - name: SonarCloud Scanner End + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" + + 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@v3 + with: + dotnet-version: '8.0.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 + + 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@v3 + with: + dotnet-version: '8.0.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: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + + SecurityCodeScan: + runs-on: windows-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup NuGet + uses: nuget/setup-nuget@v1 + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '3.1.x' + + - name: Set up projects for analysis + uses: security-code-scan/security-code-scan-add-action@v1 + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Debug + + - name: Convert SARIF for uploading to GitHub + uses: security-code-scan/security-code-scan-results-action@v1 + + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@v3 + + Codacy_Scan: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - 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: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + + build_publish: + needs: [SonarCloud, Code_Coverage, CodeQL, SecurityCodeScan, Codacy_Scan] + if: success() + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.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: Update Development Release + if: github.ref == 'refs/heads/dev' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MYGET_URL: https://www.myget.org/feed/quantalib/package/nuget/QuanTAlib + PACKAGE_VERSION: ${{ steps.gitversion.outputs.NuGetVersion }} + run: | + tag_name="development" + release_name="Development Build" + gh release delete $tag_name --yes || true + git push origin :refs/tags/$tag_name || true + gh release create $tag_name \ + --title "$release_name" \ + --notes "Latest development build from commit ${{ github.sha }} + + MyGet Package: [$MYGET_URL/$PACKAGE_VERSION]($MYGET_URL/$PACKAGE_VERSION) \n" \ + --prerelease \ + --target ${{ github.sha }} \ + 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: Publish 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: | + 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 }} \ No newline at end of file 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/Directory.Build.props b/Directory.Build.props index cb841319..65b4088f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,6 +20,33 @@ snupkg AnyCPU true + + + $(GitVersion_NuGetVersion) + $(GitVersion_AssemblySemVer) + $(GitVersion_AssemblySemFileVer) + $(GitVersion_InformationalVersion) + + + + true + link + true + true + true + none + true + true + true + false + true + false + false + false + false + true + false + true @@ -35,4 +62,6 @@ $([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0]) - \ No newline at end of file + + + diff --git a/GitVersion.yml b/GitVersion.yml index ce39df9f..6fbaef20 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -18,19 +18,28 @@ branches: source-branches: [] tracks-release-branches: false is-release-branch: true - is-main-branch: true pre-release-weight: 55000 dev: - regex: ^dev(elop)?(ment)?$ + regex: ^dev$ mode: ContinuousDelivery - label: dev + label: '' + increment: Patch + track-merge-target: true + source-branches: ['main'] + tracks-release-branches: true + is-release-branch: false + pre-release-weight: 0 + + develop: + regex: ^develop$ + mode: ContinuousDelivery + label: '' 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 ignore: diff --git a/SyntheticVendor/SyntheticVendor.cs b/SyntheticVendor/SyntheticVendor.cs index e1a7f559..60c2118a 100644 --- a/SyntheticVendor/SyntheticVendor.cs +++ b/SyntheticVendor/SyntheticVendor.cs @@ -3,18 +3,22 @@ using System.Threading; using TradingPlatform.BusinessLayer; using TradingPlatform.BusinessLayer.Integration; +using System.Diagnostics.CodeAnalysis; -namespace SyntheticVendorNamespace + +namespace SyntheticVendorNamespace; +[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] + + +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 +46,13 @@ public SyntheticVendor() 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,277 +77,277 @@ public SyntheticVendor() 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) + private MessageSymbol CreateMessageSymbol( + string id, + string name, + string exchangeId, + string assetId, + SymbolType type, + string description) + { + var messageSymbol = new MessageSymbol(id) { - 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 + 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() + public static VendorMetaData GetVendorMetaData() + { + return new VendorMetaData() { - return new VendorMetaData() + VendorName = "Synthetic Vendor", + VendorDescription = "A synthetic vendor for testing and demonstration purposes", + GetDefaultConnections = () => { - 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 }; - } - }; - } + var defaultConnection = Vendor.CreateDefaultConnectionInfo( + "Synthetic Connection", + "Synthetic Vendor", + "", // Replace with actual path if you have a logo + allowCreateCustomConnections: true + ); + return new List { defaultConnection }; + } + }; + } - private MessageSymbol CreateMessageSymbol(string id, string name, string exchangeId, string assetId, SymbolType type) + private MessageSymbol CreateMessageSymbol(string id, string name, string exchangeId, string assetId, SymbolType type) + { + return new MessageSymbol(id) { - 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, - - [EnumMember] Debentures, - [EnumMember] Bond, - [EnumMember] Swap, -*/ - }; - } + 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, + */ + }; + } - public override ConnectionResult Connect(ConnectRequestParameters connectRequestParameters) - { - // Simulating connection process - Thread.Sleep(100); // Simulate some connection delay + public override ConnectionResult Connect(ConnectRequestParameters connectRequestParameters) + { + // Simulating connection process + Thread.Sleep(100); // Simulate some connection delay - return ConnectionResult.CreateSuccess("Successfully connected to Synthetic Vendor"); - } + return ConnectionResult.CreateSuccess("Successfully connected to Synthetic Vendor"); + } - public override void Disconnect() - { - // Simulating disconnection process - Thread.Sleep(500); // Simulate some disconnection delay + 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) - }; - } - + { + State = PingEnum.Connected, + PingTime = TimeSpan.FromMilliseconds(2), + RoundTripTime = TimeSpan.FromMilliseconds(2) + }; + } - 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 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 GetExchanges(CancellationToken token) + { + return exchanges; + } - public override IList GetSymbols(CancellationToken token) - { - return symbols; - } + public override IList GetAssets(CancellationToken token) + { + return assets; + } - public override void SubscribeSymbol(SubscribeQuotesParameters parameters) - { - // Empty method for data subscription to be filled later - } + public override IList GetSymbols(CancellationToken token) + { + return symbols; + } - public override void UnSubscribeSymbol(SubscribeQuotesParameters parameters) - { - // Empty method for data unsubscription to be filled later - } + 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; + public override IList LoadHistory(HistoryRequestParameters requestParameters) + { + var historyItems = new List(); + var symbolId = requestParameters.SymbolId; - DateTime from = requestParameters.FromTime; - DateTime to = requestParameters.ToTime; + if (string.IsNullOrEmpty(symbolId)) return historyItems; - TimeSpan periodTimeSpan = requestParameters.Aggregation.GetPeriod.Duration; + DateTime from = requestParameters.FromTime; + DateTime to = requestParameters.ToTime; - // Define the maximum number of items to generate per request - const int MAX_ITEMS_PER_REQUEST = 10000; + TimeSpan periodTimeSpan = requestParameters.Aggregation.GetPeriod.Duration; - Func waveGenerator = GetWaveGenerator(symbolId); + // Define the maximum number of items to generate per request + const int MAX_ITEMS_PER_REQUEST = 10000; - DateTime currentTime = from; - while (currentTime < to) - { - DateTime intervalEnd = currentTime.AddTicks(periodTimeSpan.Ticks * MAX_ITEMS_PER_REQUEST); - if (intervalEnd > to) - intervalEnd = to; + Func waveGenerator = GetWaveGenerator(symbolId); - while (currentTime <= intervalEnd) - { - var historyItem = waveGenerator(currentTime, periodTimeSpan); //calling generator fuction - historyItems.Add(historyItem); + DateTime currentTime = from; + while (currentTime < to) + { + DateTime intervalEnd = currentTime.AddTicks(periodTimeSpan.Ticks * MAX_ITEMS_PER_REQUEST); + if (intervalEnd > to) + intervalEnd = to; - currentTime = currentTime.Add(periodTimeSpan); + while (currentTime <= intervalEnd) + { + var historyItem = waveGenerator(currentTime, periodTimeSpan); //calling generator fuction + historyItems.Add(historyItem); - if (requestParameters.CancellationToken.IsCancellationRequested) return historyItems; - } + currentTime = currentTime.Add(periodTimeSpan); - 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; - - default: return GenerateSineWave; - } + //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; + + default: return GenerateSineWave; } + } - public override HistoryMetadata GetHistoryMetadata(CancellationToken cancellationToken) + public override HistoryMetadata GetHistoryMetadata(CancellationToken cancellationToken) + { + return new HistoryMetadata() { - return new HistoryMetadata() + AllowedHistoryTypes = new HistoryType[] { - AllowedHistoryTypes = new HistoryType[] - { HistoryType.Bid, HistoryType.Ask, HistoryType.Midpoint, HistoryType.Last, HistoryType.BidAsk, HistoryType.Mark, - }, - AllowedPeriods = new Period[] - { + }, + 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, @@ -354,19 +358,19 @@ public override HistoryMetadata GetHistoryMetadata(CancellationToken cancellatio Period.WEEK1, Period.MONTH1, Period.YEAR1 - }, - UseHistoryLocalCache = false - }; - } + }, + UseHistoryLocalCache = false + }; + } -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ -/*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ + /*******************************************************************************************************************************************/ private HistoryItemBar GenerateSpike(DateTime time, TimeSpan slice) { @@ -415,8 +419,8 @@ private HistoryItemBar GenerateSpike(DateTime time, TimeSpan slice) - private static readonly double[] distributionValues = new double[] - { + private static readonly double[] distributionValues = new double[] + { 0.010, // Extreme left tail 0.050, // Left tail 0.200, // Left of center @@ -424,547 +428,547 @@ private HistoryItemBar GenerateSpike(DateTime time, TimeSpan slice) 0.200, // Right of center 0.050, // Right tail 0.010 // Extreme right tail - }; - - private HistoryItemBar GenerateDiracDelta(DateTime time, TimeSpan slice) - { - // Ensure we're working with UTC time - DateTime utcTime = time.ToUniversalTime(); + }; - // Calculate the start of the current day - DateTime dayStart = utcTime.Date; + private HistoryItemBar GenerateDiracDelta(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); - // Determine which bar of the day we're on - int barOfDay = (int)((utcTime - dayStart).Ticks / slice.Ticks); + // Calculate the start of the current day + DateTime dayStart = utcTime.Date; - double openValue, closeValue; - double scaleFactor = 100; // Scale factor to convert to percentage + // Determine which bar of the day we're on + int barOfDay = (int)((utcTime - dayStart).Ticks / slice.Ticks); - // 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; - } + double openValue, closeValue; + double scaleFactor = 100; // Scale factor to convert to percentage - 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 - }; + // 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; + private HistoryItemBar GenerateSineWave(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); - // Calculate the position within the 25-hour cycle - double cyclePosition = minutesSinceEpoch % 1500; + // 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); + // 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); - return new HistoryItemBar - { - TicksLeft = time.Ticks, - TicksRight = time.Add(slice).Ticks - 1, - Open = value, + double factor = 0.6 * Math.Abs(nextValue - value); - High = Math.Max(value, nextValue)+factor, - Low = Math.Min(value, nextValue)-factor, + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, - Close = nextValue, - Volume = Math.Abs(nextValue - value) * 100, // Volume proportional to price change - Ticks = time.Add(slice).Ticks - time.Ticks - }; - } + 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; + private HistoryItemBar GenerateSquareWave(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); - double openValue, closeValue; + // Calculate the time within the day (in hours) + double hoursInDay = utcTime.TimeOfDay.TotalHours; - if (hoursInDay < 12) - { - // First half of the day - openValue = 99; - closeValue = 100; - } - else - { - // Second half of the day - openValue = 1; - closeValue = 0.0001; - } + double openValue, closeValue; - // 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 - } + if (hoursInDay < 12) + { + // First half of the day + openValue = 99; + closeValue = 100; + } + else + { + // Second half of the day + openValue = 1; + closeValue = 0.0001; + } - 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 - }; + // 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 } - private HistoryItemBar GenerateSawtoothWave(DateTime time, TimeSpan slice) + return new HistoryItemBar { - 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; + 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 + }; + } - 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 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; - private HistoryItemBar GenerateInverseSawtoothWave(DateTime time, TimeSpan slice) + return new HistoryItemBar { - 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)); + 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 + }; + } - 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)); - private HistoryItemBar GeneratePulseWave(DateTime time, TimeSpan slice) + return new HistoryItemBar { - 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 + 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 + }; + } - 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 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 - private HistoryItemBar GenerateTriangleWave(DateTime time, TimeSpan slice) + return new HistoryItemBar { - 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; + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = 100, + Low = -100, + Close = value, + Volume = 100, + Ticks = 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 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; - private HistoryItemBar GenerateSincWave(DateTime time, TimeSpan slice) + return new HistoryItemBar { - double minutes = (time - DateTime.UnixEpoch).TotalMinutes; - double period = 1500.0; // 24-hour period - double frequency = 2 * Math.PI / period; // Full cycle over 24 hours + 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 + }; + } - // Adjust time to center the main peak at 12 hours - double t = minutes % period - period / 2; + 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 - // Scale factor - double scaleFactor = 7.0; + // Adjust time to center the main peak at 12 hours + double t = minutes % period - period / 2; - // Calculate Sinc value - double x = scaleFactor * frequency * t; - double sincValue = x != 0 ? 100 * Math.Sin(x) / x : 100; + // Scale factor + double scaleFactor = 7.0; - // 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; + // Calculate Sinc value + double x = scaleFactor * frequency * t; + double sincValue = x != 0 ? 100 * Math.Sin(x) / x : 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); + // 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; - 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 - }; - } + // 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); - private HistoryItemBar GenerateGaussianPulse(DateTime time, TimeSpan slice) + return new HistoryItemBar { - 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; + 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 + }; + } - // 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) + 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; - double baselineValue = 0.00001; // Value outside the pulse period + // 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) - // 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; - } + double baselineValue = 0.00001; // Value outside the pulse period - // 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; - } + // 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; + } - 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 - }; + // 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; } - private HistoryItemBar GenerateFrequencySweep(DateTime time, TimeSpan slice) + return new HistoryItemBar { - double hours = (time - DateTime.UnixEpoch).TotalHours; - double sweepPeriod = 48.0; // 48-hour period + 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 + }; + } - // Starting frequency (very low) - double minFreq = Math.PI / 48.0; + private HistoryItemBar GenerateFrequencySweep(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double sweepPeriod = 48.0; // 48-hour period - // Calculate the ending frequency to ensure continuity - double maxFreq = Math.PI * 1.0 * Math.Exp(2 * Math.PI / sweepPeriod); + // Starting frequency (very low) + double minFreq = Math.PI / 48.0; - // Calculate the exponential factor for frequency sweep - double expFactor = Math.Log(maxFreq / minFreq) / sweepPeriod; + // Calculate the ending frequency to ensure continuity + double maxFreq = Math.PI * 1.0 * Math.Exp(2 * Math.PI / sweepPeriod); - // Calculate the overall phase up to the current time - double totalPhase = (minFreq / expFactor) * (Math.Exp(expFactor * (hours % sweepPeriod)) - 1); + // Calculate the exponential factor for frequency sweep + double expFactor = Math.Log(maxFreq / minFreq) / sweepPeriod; - // Shift the phase to start the cycle at 100 (cosine-like behavior) - totalPhase += Math.PI / 2; + // Calculate the overall phase up to the current time + double totalPhase = (minFreq / expFactor) * (Math.Exp(expFactor * (hours % sweepPeriod)) - 1); - // Calculate the value of the signal at the current time - double value = 100.0 * Math.Sin(totalPhase); + // Shift the phase to start the cycle at 100 (cosine-like behavior) + totalPhase += Math.PI / 2; - // 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); + // Calculate the value of the signal at the current time + double value = 100.0 * Math.Sin(totalPhase); - 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 - }; - } + // 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(); + // 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 + 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; + // 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); + // 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; - } + // 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 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); + // 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 - }; + // 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; - } + 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 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; + 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 + // 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 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 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); + // 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); + // 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; + // 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 - }; - } + 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; + 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); + 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); + // 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; + // 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 lowNoise = Math.Abs(random.NextDouble()); + low -= volatility * lowNoise; - double volume = Math.Abs(random.NextDouble()) * 1000 + 100; + double volume = Math.Abs(random.NextDouble()) * 1000 + 100; - previousClose = close; + 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 - }; + // 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; - } + return historyItem; + } -private double previousClose = 50; -private const double meanPrice = 50; + private double previousClose = 50; + private const double meanPrice = 50; -private HistoryItemBar GeneratePinkNoise(DateTime time, TimeSpan slice) -{ - double volatility = 2; - double meanReversionStrength = 0.1; + 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 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); + // 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); + // 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; + // Add variation to High and Low + double highNoise = Math.Abs(GeneratePinkNoiseValue()); + high += volatility * highNoise; - double lowNoise = Math.Abs(GeneratePinkNoiseValue()); - low -= volatility * lowNoise; + double lowNoise = Math.Abs(GeneratePinkNoiseValue()); + low -= volatility * lowNoise; - double volume = Math.Abs(GeneratePinkNoiseValue()) * 1000 + 100; + double volume = Math.Abs(GeneratePinkNoiseValue()) * 1000 + 100; - // Update previous close for the next iteration - previousClose = close; + // 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 - }; -} + 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; @@ -986,154 +990,153 @@ private double GeneratePinkNoiseValue() -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 + 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; + 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)); + // 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; + lastValue = close; - return new HistoryItemBar + return new HistoryItemBar + { + 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) { - 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 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 double GBMMu = 0.05; // Annual drift -private double GBMSigma = 0.2; // Annual volatility + private double GBMLastClose = 100; // Starting price + private double GBMMu = 0.05; // Annual drift + private double GBMSigma = 0.2; // Annual volatility -private HistoryItemBar GenerateGBM(DateTime time, TimeSpan slice) -{ - // Convert time slice to years - double dt = slice.TotalDays / 365.0; + 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); + // 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); + // 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; + // 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; + // 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 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)); + // 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; + // 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); + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + } - double epsilon = GenerateFractionalGaussianNoise(FBMHurst); + 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 - double drift = FBMDrift * dt; - double diffusion = FBMSigma * Math.Pow(dt, FBMHurst) * epsilon; + private HistoryItemBar GenerateFBM(DateTime time, TimeSpan slice) + { + double dt = Math.Pow(slice.TotalDays / 365.0, 0.5); - double openVariability = FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * GenerateFractionalGaussianNoise(FBMHurst) * 0.1; - double open = FBMLastClose + openVariability; + double epsilon = GenerateFractionalGaussianNoise(FBMHurst); - double close = open * Math.Exp(drift + diffusion); + double drift = FBMDrift * dt; + double diffusion = FBMSigma * Math.Pow(dt, FBMHurst) * epsilon; - 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 openVariability = FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * GenerateFractionalGaussianNoise(FBMHurst) * 0.1; + double open = FBMLastClose + openVariability; - double volume = Math.Max(100, 2000 * Math.Abs(close - open) + - 1000 * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst))); + double close = open * Math.Exp(drift + diffusion); - FBMLastClose = close; + 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; - 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 - }; - } + double volume = Math.Max(100, 2000 * Math.Abs(close - open) + + 1000 * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst))); + + FBMLastClose = close; - private double GenerateFractionalGaussianNoise(double hurst) + return new HistoryItemBar { - double sum = 0; - int n = 1000; // Number of terms in the approximation + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + } - 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; - } + private double GenerateFractionalGaussianNoise(double hurst) + { + double sum = 0; + int n = 1000; // Number of terms in the approximation - return sum / Math.Sqrt(n); + 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 - } -} \ No newline at end of file + + // Add other necessary overrides and implementations as needed +} diff --git a/Tests/test_Trady.cs b/Tests/test_Trady.cs index ebf70c4e..44451a2a 100644 --- a/Tests/test_Trady.cs +++ b/Tests/test_Trady.cs @@ -2,7 +2,11 @@ using Trady.Analysis.Indicator; using Trady.Core; using Trady.Core.Infrastructure; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; + +namespace QuanTAlib; + +[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] public class TradyTests { diff --git a/Tests/test_Tulip.cs b/Tests/test_Tulip.cs index 174b4046..623f0595 100644 --- a/Tests/test_Tulip.cs +++ b/Tests/test_Tulip.cs @@ -1,7 +1,10 @@ using Xunit; using Tulip; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; +namespace QuanTAlib; + +[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] public class TulipTests { private readonly TBarSeries bars; @@ -71,10 +74,7 @@ public void EMA() double QL_item = QL[i].Value; double TU = arrout[0][i]; 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_iTBar.cs b/Tests/test_iTBar.cs index db5d70ae..3c8afe4a 100644 --- a/Tests/test_iTBar.cs +++ b/Tests/test_iTBar.cs @@ -1,58 +1,60 @@ using Xunit; using System.Reflection; +using System.Diagnostics.CodeAnalysis; -namespace QuanTAlib +namespace QuanTAlib; + +[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] + +public class BarIndicatorTests { - public class BarIndicatorTests - { - private readonly Random rnd; - private const int SeriesLen = 1000; - private const int Corrections = 100; + private readonly Random rnd; + private const int SeriesLen = 1000; + private const int Corrections = 100; - public BarIndicatorTests() - { - rnd = new Random((int)DateTime.Now.Ticks); - } + public BarIndicatorTests() + { + rnd = new Random((int)DateTime.Now.Ticks); + } - private static readonly iTValue[] indicators = new iTValue[] - { + private static readonly iTValue[] indicators = new iTValue[] + { new Atr(period: 14), - }; + }; + + [Theory] + [MemberData(nameof(GetIndicators))] + public void IndicatorIsNew(iTValue indicator) + { + var indicator1 = indicator; + var indicator2 = indicator; - [Theory] - [MemberData(nameof(GetIndicators))] - public void IndicatorIsNew(iTValue indicator) + MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!; + if (calcMethod == null) { - var indicator1 = indicator; - var indicator2 = indicator; + throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}"); + } - MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!; - if (calcMethod == null) - { - throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}"); - } + for (int i = 0; i < SeriesLen; i++) + { + TBar item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: true); + calcMethod.Invoke(indicator1, new object[] { item1 }); - for (int i = 0; i < SeriesLen; i++) + for (int j = 0; j < Corrections; j++) { - TBar item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: true); + item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: false); calcMethod.Invoke(indicator1, new object[] { item1 }); + } - for (int j = 0; j < Corrections; j++) - { - item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: false); - calcMethod.Invoke(indicator1, new object[] { item1 }); - } - - var item2 = new TBar (item1.Time, item1.Open, item1.High, item1.Low, item1.Close, item1.Volume , IsNew: true); - calcMethod.Invoke(indicator2, new object[] { item2 }); + var item2 = new TBar(item1.Time, item1.Open, item1.High, item1.Low, item1.Close, item1.Volume, IsNew: true); + calcMethod.Invoke(indicator2, new object[] { item2 }); - Assert.Equal(indicator1.Value, indicator2.Value); - } + Assert.Equal(indicator1.Value, indicator2.Value); } + } - public static IEnumerable GetIndicators() - { - return indicators.Select(indicator => new object[] { indicator }); - } + public static IEnumerable GetIndicators() + { + return indicators.Select(indicator => new object[] { indicator }); } -} \ No newline at end of file +} diff --git a/Tests/test_iTValue.cs b/Tests/test_iTValue.cs index e1b33a98..4e5ceeb8 100644 --- a/Tests/test_iTValue.cs +++ b/Tests/test_iTValue.cs @@ -1,22 +1,25 @@ using Xunit; using System.Reflection; +using System.Diagnostics.CodeAnalysis; -namespace QuanTAlib +namespace QuanTAlib; + +[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] + +public class IndicatorTests { - public class IndicatorTests - { - private readonly Random rnd; - private const int SeriesLen = 1000; - private const int Corrections = 100; + private readonly Random rnd; + private const int SeriesLen = 1000; + private const int Corrections = 100; - public IndicatorTests() - { - rnd = new Random((int)DateTime.Now.Ticks); - } + public IndicatorTests() + { + rnd = new Random((int)DateTime.Now.Ticks); + } - private static readonly iTValue[] indicators = - [ - new Ema(period: 10, useSma: true), + private static readonly iTValue[] indicators = + [ + new Ema(period: 10, useSma: true), new Alma(period: 14, offset: 0.85, sigma: 6), new Afirma(periods: 4, taps: 4, window: Afirma.WindowType.Blackman), new Convolution(new double[] { 1.0, 2, 3, 2, 1 }), @@ -57,42 +60,41 @@ public IndicatorTests() new Variance(period: 14), new Zscore(period: 14) - ]; + ]; - [Theory] - [MemberData(nameof(GetIndicators))] - public void IndicatorIsNew(iTValue indicator) + [Theory] + [MemberData(nameof(GetIndicators))] + public void IndicatorIsNew(iTValue indicator) + { + var indicator1 = indicator; + var indicator2 = indicator; + + MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!; + if (calcMethod == null) { - var indicator1 = indicator; - var indicator2 = indicator; + throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}"); + } - MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!; - if (calcMethod == null) - { - throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}"); - } + for (int i = 0; i < SeriesLen; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + calcMethod.Invoke(indicator1, new object[] { item1 }); - for (int i = 0; i < SeriesLen; i++) + for (int j = 0; j < Corrections; j++) { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); calcMethod.Invoke(indicator1, new object[] { item1 }); + } - for (int j = 0; j < Corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - calcMethod.Invoke(indicator1, new object[] { item1 }); - } - - var item2 = new TValue(item1.Time, item1.Value, IsNew: true); - calcMethod.Invoke(indicator2, new object[] { item2 }); + var item2 = new TValue(item1.Time, item1.Value, IsNew: true); + calcMethod.Invoke(indicator2, new object[] { item2 }); - Assert.Equal(indicator1.Value, indicator2.Value); - } + Assert.Equal(indicator1.Value, indicator2.Value); } + } - public static IEnumerable GetIndicators() - { - return indicators.Select(indicator => new object[] { indicator }); - } + public static IEnumerable GetIndicators() + { + return indicators.Select(indicator => new object[] { indicator }); } -} \ No newline at end of file +} diff --git a/Tests/test_skender.stock.cs b/Tests/test_skender.stock.cs index 7c198cb7..65bc9f7d 100644 --- a/Tests/test_skender.stock.cs +++ b/Tests/test_skender.stock.cs @@ -1,6 +1,10 @@ using Xunit; using Skender.Stock.Indicators; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; + +namespace QuanTAlib; + +[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] public class SkenderTests { diff --git a/Tests/test_talib.cs b/Tests/test_talib.cs index dcd64c32..cc0f6682 100644 --- a/Tests/test_talib.cs +++ b/Tests/test_talib.cs @@ -1,6 +1,10 @@ using Xunit; using TALib; -using QuanTAlib; +using System.Diagnostics.CodeAnalysis; + +namespace QuanTAlib; + +[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] public class TAlibTests {