[
  {
    "path": ".gitignore",
    "content": "## Custom\nTests/\nCache/\nSubmodules/\nkeys.bin\nvirtual-account.json\nexchange-account.json\n\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n**/Properties/launchSettings.json\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n#*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk \n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output \nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# VScode\n*vscode*\n.DS_Store\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"Submodules/ExchangeSharp\"]\n\tpath = Submodules/ExchangeSharp\n\turl = https://github.com/jazzonaut/ExchangeSharp\n\tbranch = master\n"
  },
  {
    "path": "Disclaimer.txt",
    "content": "By using, or simply downloading IntelliTrader (the Software), you understand and accept the following:\n\n\nLicensing\n\nThe Software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International licence. Full terms of the licence can be found by following this link:\n\nhttps://creativecommons.org/licenses/by-nc-sa/4.0/legalcode\n\nA full copy of the license is also included with the download.\n\n\nLimitation Of Liability\n\nIn no event shall liability be accepted for any indirect, incidental, special, consequential or punitive damages, including, without limitation, loss of profits, funds, data, use, goodwill, or other tangible and intangible losses, resulting from:\n\n\t(i) your access to, or use of, or inability to access or use the Software; \n\t(ii) any content of any third party used with the Software; \n\t(iii) any content obtained from the Software; and \n\t(iv) unauthorized access, use or alteration of your transmissions or content, whether based on warranty, contract, tort (including negligence) or any other legal theory, whether or not we have been informed of the possibility of such damage, and even if a remedy set forth herein is found to have failed of its essential purpose.\n\nWe do not refund losses.\n\n\nDisclaimer\n\nYour use of the Software is at your sole risk. The Software is provided on an “AS IS” and “AS AVAILABLE” basis. The Software is provided without warranties of any kind, whether expressed or implied, including, but not limited to, implied warranties of merchantability, fitness for a particular purpose, non-infringement or course of performance.\n\nNo warranty of any kind is expressed or implied, that:\n\n\ta) the Software will function, be secure or operate on any nominated platform; \n\tb) any errors or defects will be corrected; \n\tc) the Software is free of viruses or other harmful components; or \n\td) the results of using the Software will meet your requirements.\n\n\nIf you do not agree with any of the above, please do not download or use the Software."
  },
  {
    "path": "IntelliTrader/Help.url",
    "content": "[InternetShortcut]\nURL=https://github.com/jazzonaut/IntelliTrader/wiki\nIconFile=https://assets-cdn.github.com/favicon.ico\nIconIndex=1\n"
  },
  {
    "path": "IntelliTrader/IntelliTrader.Web.deps.json",
    "content": "{\n  \"runtimeTarget\": {\n    \"name\": \".NETCoreApp,Version=v2.1\",\n    \"signature\": \"53477019973cf406227ea0f01f2112897f901df8\"\n  },\n  \"compilationOptions\": {\n    \"defines\": [\n      \"TRACE\",\n      \"RELEASE\",\n      \"NETCOREAPP\",\n      \"NETCOREAPP2_1\"\n    ],\n    \"languageVersion\": \"\",\n    \"platform\": \"\",\n    \"allowUnsafe\": false,\n    \"warningsAsErrors\": false,\n    \"optimize\": true,\n    \"keyFile\": \"\",\n    \"emitEntryPoint\": false,\n    \"xmlDoc\": false,\n    \"debugType\": \"portable\"\n  },\n  \"targets\": {\n    \".NETCoreApp,Version=v2.1\": {\n      \"IntelliTrader.Web/1.0.0\": {\n        \"dependencies\": {\n          \"IntelliTrader.Core\": \"1.0.0\",\n          \"Microsoft.AspNetCore.Authentication\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Authentication.Cookies\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Diagnostics\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Server.Kestrel\": \"2.1.2\",\n          \"Microsoft.AspNetCore.StaticFiles\": \"2.1.1\",\n          \"Microsoft.NETCore.App\": \"2.1.0\"\n        },\n        \"runtime\": {\n          \"IntelliTrader.Web.dll\": {}\n        },\n        \"compile\": {\n          \"IntelliTrader.Web.dll\": {}\n        }\n      },\n      \"Autofac/4.8.1\": {\n        \"dependencies\": {\n          \"NETStandard.Library\": \"2.0.3\",\n          \"System.ComponentModel\": \"4.0.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.1/Autofac.dll\": {\n            \"assemblyVersion\": \"4.8.1.0\",\n            \"fileVersion\": \"4.8.1.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.1/Autofac.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Antiforgery/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.DataProtection\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"2.1.1\",\n          \"Microsoft.Extensions.ObjectPool\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Authentication/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Authentication.Core\": \"2.1.1\",\n          \"Microsoft.AspNetCore.DataProtection\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\",\n          \"Microsoft.Extensions.WebEncoders\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Authentication.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Authentication.Cookies/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Authentication\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Authentication.Core/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Authentication.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Authorization/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Authorization.Policy/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Authentication.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Authorization\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Connections.Abstractions/2.1.2\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"2.1.1\",\n          \"System.IO.Pipelines\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.2.0\",\n            \"fileVersion\": \"2.1.2.18180\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Cors/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Cryptography.Internal/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.DataProtection/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Cryptography.Internal\": \"2.1.1\",\n          \"Microsoft.AspNetCore.DataProtection.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\",\n          \"Microsoft.Win32.Registry\": \"4.5.0\",\n          \"System.Security.Cryptography.Xml\": \"4.5.0\",\n          \"System.Security.Principal.Windows\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.DataProtection.Abstractions/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Diagnostics/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Diagnostics.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"2.1.1\",\n          \"Microsoft.Extensions.FileProviders.Physical\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.5.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Diagnostics.Abstractions/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.EnvironmentVariables\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.FileExtensions\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection\": \"2.1.1\",\n          \"Microsoft.Extensions.FileProviders.Physical\": \"2.1.1\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.5.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting.Server.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Html.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"System.Text.Encodings.Web\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Http/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"2.1.1\",\n          \"Microsoft.Extensions.ObjectPool\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\",\n          \"Microsoft.Net.Http.Headers\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"2.1.1\",\n          \"System.Text.Encodings.Web\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Extensions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.1.1\",\n          \"Microsoft.Net.Http.Headers\": \"2.1.1\",\n          \"System.Buffers\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Features/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.JsonPatch/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.5.0\",\n          \"Newtonsoft.Json\": \"11.0.2\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Localization/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.Extensions.Localization.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Mvc.ApiExplorer\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Cors\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.DataAnnotations\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Formatters.Json\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Localization\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Razor.Extensions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.RazorPages\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.TagHelpers\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.ViewFeatures\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Razor.Design\": \"2.1.1\",\n          \"Microsoft.Extensions.Caching.Memory\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Routing.Abstractions\": \"2.1.1\",\n          \"Microsoft.Net.Http.Headers\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.ApiExplorer/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Mvc.Core\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.Core/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Authentication.Core\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Authorization.Policy\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.ResponseCaching.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Routing\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyModel\": \"2.1.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.5.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.Cors/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Cors\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Core\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.DataAnnotations/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Mvc.Core\": \"2.1.1\",\n          \"Microsoft.Extensions.Localization\": \"2.1.1\",\n          \"System.ComponentModel.Annotations\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.Formatters.Json/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.JsonPatch\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Core\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.Localization/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Localization\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Razor\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection\": \"2.1.1\",\n          \"Microsoft.Extensions.Localization\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.Razor/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Mvc.Razor.Extensions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.ViewFeatures\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Razor.Runtime\": \"2.1.1\",\n          \"Microsoft.CodeAnalysis.CSharp\": \"2.8.0\",\n          \"Microsoft.CodeAnalysis.Razor\": \"2.1.1\",\n          \"Microsoft.Extensions.Caching.Memory\": \"2.1.1\",\n          \"Microsoft.Extensions.FileProviders.Composite\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.Razor.Extensions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Razor.Language\": \"2.1.1\",\n          \"Microsoft.CodeAnalysis.Razor\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.RazorPages/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Mvc.Razor\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.TagHelpers/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Mvc.Razor\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Razor.Runtime\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Routing.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Caching.Memory\": \"2.1.1\",\n          \"Microsoft.Extensions.FileSystemGlobbing\": \"2.1.1\",\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Mvc.ViewFeatures/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Antiforgery\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Diagnostics.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Html.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Core\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.DataAnnotations\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Mvc.Formatters.Json\": \"2.1.1\",\n          \"Microsoft.Extensions.WebEncoders\": \"2.1.1\",\n          \"Newtonsoft.Json.Bson\": \"1.0.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Razor/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Html.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Razor.Design/2.1.1\": {},\n      \"Microsoft.AspNetCore.Razor.Language/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Razor.Runtime/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Html.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Razor\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.ResponseCaching.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Routing/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Routing.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.ObjectPool\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Routing.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Routing.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Routing.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Server.Kestrel/2.1.2\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Server.Kestrel.Core\": \"2.1.2\",\n          \"Microsoft.AspNetCore.Server.Kestrel.Https\": \"2.1.2\",\n          \"Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets\": \"2.1.2\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll\": {\n            \"assemblyVersion\": \"2.1.2.0\",\n            \"fileVersion\": \"2.1.2.18180\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Server.Kestrel.Core/2.1.2\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions\": \"2.1.2\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.Binder\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\",\n          \"Microsoft.Net.Http.Headers\": \"2.1.1\",\n          \"System.Memory\": \"4.5.1\",\n          \"System.Numerics.Vectors\": \"4.5.0\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.5.1\",\n          \"System.Security.Cryptography.Cng\": \"4.5.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.1\"\n        },\n        \"runtime\": {\n          \"lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll\": {\n            \"assemblyVersion\": \"2.1.2.0\",\n            \"fileVersion\": \"2.1.2.18180\"\n          }\n        },\n        \"compile\": {\n          \"lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Server.Kestrel.Https/2.1.2\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Server.Kestrel.Core\": \"2.1.2\"\n        },\n        \"runtime\": {\n          \"lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll\": {\n            \"assemblyVersion\": \"2.1.2.0\",\n            \"fileVersion\": \"2.1.2.18180\"\n          }\n        },\n        \"compile\": {\n          \"lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.1.2\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Connections.Abstractions\": \"2.1.2\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.2.0\",\n            \"fileVersion\": \"2.1.2.18180\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.1.2\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions\": \"2.1.2\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll\": {\n            \"assemblyVersion\": \"2.1.2.0\",\n            \"fileVersion\": \"2.1.2.18180\"\n          }\n        },\n        \"compile\": {\n          \"lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.StaticFiles/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.1.1\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.1.1\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.WebEncoders\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll\": {}\n        }\n      },\n      \"Microsoft.AspNetCore.WebUtilities/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Net.Http.Headers\": \"2.1.1\",\n          \"System.Text.Encodings.Web\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll\": {}\n        }\n      },\n      \"Microsoft.CodeAnalysis.Analyzers/1.1.0\": {},\n      \"Microsoft.CodeAnalysis.Common/2.8.0\": {\n        \"dependencies\": {\n          \"Microsoft.CodeAnalysis.Analyzers\": \"1.1.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Collections.Immutable\": \"1.3.1\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.FileVersionInfo\": \"4.3.0\",\n          \"System.Diagnostics.StackTrace\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Dynamic.Runtime\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.CodePages\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Parallel\": \"4.3.0\",\n          \"System.Threading.Thread\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\",\n          \"System.Xml.XPath.XDocument\": \"4.3.0\",\n          \"System.Xml.XmlDocument\": \"4.3.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Microsoft.CodeAnalysis.dll\": {\n            \"assemblyVersion\": \"2.8.0.0\",\n            \"fileVersion\": \"2.8.0.62830\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Microsoft.CodeAnalysis.dll\": {}\n        }\n      },\n      \"Microsoft.CodeAnalysis.CSharp/2.8.0\": {\n        \"dependencies\": {\n          \"Microsoft.CodeAnalysis.Common\": \"2.8.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll\": {\n            \"assemblyVersion\": \"2.8.0.0\",\n            \"fileVersion\": \"2.8.0.62830\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll\": {}\n        }\n      },\n      \"Microsoft.CodeAnalysis.Razor/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Razor.Language\": \"2.1.1\",\n          \"Microsoft.CodeAnalysis.CSharp\": \"2.8.0\",\n          \"Microsoft.CodeAnalysis.Common\": \"2.8.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll\": {}\n        }\n      },\n      \"Microsoft.CSharp/4.5.0\": {},\n      \"Microsoft.DotNet.PlatformAbstractions/2.1.0\": {\n        \"dependencies\": {\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll\": {\n            \"assemblyVersion\": \"2.1.0.0\",\n            \"fileVersion\": \"2.1.0.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Caching.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Caching.Memory/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Caching.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Configuration/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Binder/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Configuration.EnvironmentVariables/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Configuration.FileExtensions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"2.1.1\",\n          \"Microsoft.Extensions.FileProviders.Physical\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Json/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.FileExtensions\": \"2.1.1\",\n          \"Newtonsoft.Json\": \"11.0.2\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.DependencyModel/2.1.0\": {\n        \"dependencies\": {\n          \"Microsoft.DotNet.PlatformAbstractions\": \"2.1.0\",\n          \"Newtonsoft.Json\": \"11.0.2\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Dynamic.Runtime\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll\": {\n            \"assemblyVersion\": \"2.1.0.0\",\n            \"fileVersion\": \"2.1.0.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Composite/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Physical/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.FileSystemGlobbing\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.FileSystemGlobbing/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Localization/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Localization.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Localization.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Localization.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Localization.Abstractions/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Logging/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Binder\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Logging.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Logging.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.ObjectPool/2.1.1\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Options/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Options.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Options.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.Primitives/2.1.1\": {\n        \"dependencies\": {\n          \"System.Memory\": \"4.5.1\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.5.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Primitives.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.Primitives.dll\": {}\n        }\n      },\n      \"Microsoft.Extensions.WebEncoders/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\",\n          \"System.Text.Encodings.Web\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll\": {}\n        }\n      },\n      \"Microsoft.Net.Http.Headers/2.1.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\",\n          \"System.Buffers\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/Microsoft.Net.Http.Headers.dll\": {\n            \"assemblyVersion\": \"2.1.1.0\",\n            \"fileVersion\": \"2.1.1.18157\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Microsoft.Net.Http.Headers.dll\": {}\n        }\n      },\n      \"Microsoft.Win32.Registry/4.5.0\": {\n        \"dependencies\": {\n          \"System.Security.AccessControl\": \"4.5.0\",\n          \"System.Security.Principal.Windows\": \"4.5.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        },\n        \"compile\": {\n          \"ref/netstandard2.0/Microsoft.Win32.Registry.dll\": {}\n        }\n      },\n      \"Newtonsoft.Json/11.0.2\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/Newtonsoft.Json.dll\": {\n            \"assemblyVersion\": \"11.0.0.0\",\n            \"fileVersion\": \"11.0.2.21924\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/Newtonsoft.Json.dll\": {}\n        }\n      },\n      \"Newtonsoft.Json.Bson/1.0.1\": {\n        \"dependencies\": {\n          \"NETStandard.Library\": \"2.0.3\",\n          \"Newtonsoft.Json\": \"11.0.2\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Newtonsoft.Json.Bson.dll\": {\n            \"assemblyVersion\": \"1.0.0.0\",\n            \"fileVersion\": \"1.0.1.20722\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Newtonsoft.Json.Bson.dll\": {}\n        }\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/debian.8-x64/native/_._\": {\n            \"rid\": \"debian.8-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/fedora.23-x64/native/_._\": {\n            \"rid\": \"fedora.23-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/fedora.24-x64/native/_._\": {\n            \"rid\": \"fedora.24-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.native.System/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple/4.3.0\": {\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/opensuse.13.2-x64/native/_._\": {\n            \"rid\": \"opensuse.13.2-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/opensuse.42.1-x64/native/_._\": {\n            \"rid\": \"opensuse.42.1-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/osx.10.10-x64/native/_._\": {\n            \"rid\": \"osx.10.10-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/osx.10.10-x64/native/_._\": {\n            \"rid\": \"osx.10.10-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/rhel.7-x64/native/_._\": {\n            \"rid\": \"rhel.7-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/ubuntu.14.04-x64/native/_._\": {\n            \"rid\": \"ubuntu.14.04-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/ubuntu.16.04-x64/native/_._\": {\n            \"rid\": \"ubuntu.16.04-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"runtimeTargets\": {\n          \"runtime/ubuntu.16.10-x64/native/_._\": {\n            \"rid\": \"ubuntu.16.10-x64\",\n            \"assetType\": \"native\"\n          }\n        }\n      },\n      \"Serilog/2.7.1\": {\n        \"dependencies\": {\n          \"NETStandard.Library\": \"2.0.3\",\n          \"System.Collections.NonGeneric\": \"4.3.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Serilog.dll\": {\n            \"assemblyVersion\": \"2.0.0.0\",\n            \"fileVersion\": \"2.7.1.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Serilog.dll\": {}\n        }\n      },\n      \"Serilog.Enrichers.Environment/2.1.2\": {\n        \"dependencies\": {\n          \"Serilog\": \"2.7.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Serilog.Enrichers.Environment.dll\": {\n            \"assemblyVersion\": \"2.0.0.0\",\n            \"fileVersion\": \"2.1.2.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Serilog.Enrichers.Environment.dll\": {}\n        }\n      },\n      \"Serilog.Filters.Expressions/2.0.0\": {\n        \"dependencies\": {\n          \"NETStandard.Library\": \"2.0.3\",\n          \"Serilog\": \"2.7.1\",\n          \"Superpower\": \"2.0.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.5/Serilog.Filters.Expressions.dll\": {\n            \"assemblyVersion\": \"2.0.0.0\",\n            \"fileVersion\": \"2.0.0.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.5/Serilog.Filters.Expressions.dll\": {}\n        }\n      },\n      \"Serilog.Settings.Configuration/2.6.1\": {\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyModel\": \"2.1.0\",\n          \"Serilog\": \"2.7.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.6/Serilog.Settings.Configuration.dll\": {\n            \"assemblyVersion\": \"2.6.1.0\",\n            \"fileVersion\": \"2.6.1.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.6/Serilog.Settings.Configuration.dll\": {}\n        }\n      },\n      \"Serilog.Sinks.Console/3.1.1\": {\n        \"dependencies\": {\n          \"Serilog\": \"2.7.1\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\"\n        },\n        \"runtime\": {\n          \"lib/netcoreapp1.1/Serilog.Sinks.Console.dll\": {\n            \"assemblyVersion\": \"3.1.1.0\",\n            \"fileVersion\": \"3.1.1.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netcoreapp1.1/Serilog.Sinks.Console.dll\": {}\n        }\n      },\n      \"Serilog.Sinks.File/3.2.0\": {\n        \"dependencies\": {\n          \"Serilog\": \"2.7.1\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.0.1\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Serilog.Sinks.File.dll\": {\n            \"assemblyVersion\": \"2.0.0.0\",\n            \"fileVersion\": \"3.2.0.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Serilog.Sinks.File.dll\": {}\n        }\n      },\n      \"Serilog.Sinks.RollingFile/3.3.0\": {\n        \"dependencies\": {\n          \"Serilog.Sinks.File\": \"3.2.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.3/Serilog.Sinks.RollingFile.dll\": {\n            \"assemblyVersion\": \"2.0.0.0\",\n            \"fileVersion\": \"3.3.0.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.3/Serilog.Sinks.RollingFile.dll\": {}\n        }\n      },\n      \"Superpower/2.0.0\": {\n        \"dependencies\": {\n          \"NETStandard.Library\": \"2.0.3\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.0/Superpower.dll\": {\n            \"assemblyVersion\": \"1.0.0.0\",\n            \"fileVersion\": \"2.0.0.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.0/Superpower.dll\": {}\n        }\n      },\n      \"System.AppContext/4.3.0\": {\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers/4.5.0\": {},\n      \"System.Collections/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable/1.3.1\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.NonGeneric/4.3.0\": {\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel/4.0.1\": {\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.Annotations/4.5.0\": {},\n      \"System.Console/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource/4.5.0\": {},\n      \"System.Diagnostics.FileVersionInfo/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Diagnostics.StackTrace/4.3.0\": {\n        \"dependencies\": {\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.IO/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Buffers\": \"4.5.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.IO.FileSystem/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives/4.3.0\": {\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Pipelines/4.5.0\": {\n        \"runtime\": {\n          \"lib/netcoreapp2.1/System.IO.Pipelines.dll\": {\n            \"assemblyVersion\": \"4.0.0.0\",\n            \"fileVersion\": \"4.6.26515.6\"\n          }\n        },\n        \"compile\": {\n          \"ref/netstandard1.3/System.IO.Pipelines.dll\": {}\n        }\n      },\n      \"System.Linq/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Memory/4.5.1\": {},\n      \"System.Net.Http/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.5.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Net.Primitives/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Requests/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.WebHeaderCollection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Net.WebHeaderCollection/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Numerics.Vectors/4.5.0\": {},\n      \"System.ObjectModel/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit/4.3.0\": {\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration/4.3.0\": {\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight/4.3.0\": {\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Metadata/1.6.0\": {},\n      \"System.Reflection.Primitives/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions/4.3.0\": {\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\"\n        }\n      },\n      \"System.Runtime.CompilerServices.Unsafe/4.5.1\": {\n        \"runtime\": {\n          \"lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll\": {\n            \"assemblyVersion\": \"4.0.4.0\",\n            \"fileVersion\": \"0.0.0.0\"\n          }\n        },\n        \"compile\": {\n          \"ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll\": {}\n        }\n      },\n      \"System.Runtime.Extensions/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation/4.3.0\": {\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Runtime.Numerics/4.3.0\": {\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.AccessControl/4.5.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Security.Principal.Windows\": \"4.5.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        },\n        \"compile\": {\n          \"ref/netstandard2.0/System.Security.AccessControl.dll\": {}\n        }\n      },\n      \"System.Security.Cryptography.Algorithms/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/osx/lib/_._\": {\n            \"rid\": \"osx\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Security.Cryptography.Cng/4.5.0\": {\n        \"runtimeTargets\": {\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        },\n        \"compile\": {\n          \"ref/netcoreapp2.1/System.Security.Cryptography.Cng.dll\": {}\n        }\n      },\n      \"System.Security.Cryptography.Csp/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Security.Cryptography.Encoding/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Security.Cryptography.Pkcs/4.5.0\": {\n        \"dependencies\": {\n          \"System.Security.Cryptography.Cng\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll\": {\n            \"assemblyVersion\": \"4.0.3.0\",\n            \"fileVersion\": \"4.6.26515.6\"\n          }\n        },\n        \"runtimeTargets\": {\n          \"runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\",\n            \"assemblyVersion\": \"4.0.3.0\",\n            \"fileVersion\": \"4.6.26515.6\"\n          }\n        }\n      },\n      \"System.Security.Cryptography.Primitives/4.3.0\": {\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.5.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        }\n      },\n      \"System.Security.Cryptography.Xml/4.5.0\": {\n        \"dependencies\": {\n          \"System.Security.Cryptography.Pkcs\": \"4.5.0\",\n          \"System.Security.Permissions\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/System.Security.Cryptography.Xml.dll\": {\n            \"assemblyVersion\": \"4.0.1.0\",\n            \"fileVersion\": \"4.6.26515.6\"\n          }\n        },\n        \"compile\": {\n          \"ref/netstandard2.0/System.Security.Cryptography.Xml.dll\": {}\n        }\n      },\n      \"System.Security.Permissions/4.5.0\": {\n        \"dependencies\": {\n          \"System.Security.AccessControl\": \"4.5.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard2.0/System.Security.Permissions.dll\": {\n            \"assemblyVersion\": \"4.0.1.0\",\n            \"fileVersion\": \"4.6.26515.6\"\n          }\n        },\n        \"compile\": {\n          \"ref/netstandard2.0/System.Security.Permissions.dll\": {}\n        }\n      },\n      \"System.Security.Principal.Windows/4.5.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtime/unix/lib/_._\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\"\n          },\n          \"runtime/win/lib/_._\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\"\n          }\n        },\n        \"compile\": {\n          \"ref/netstandard2.0/System.Security.Principal.Windows.dll\": {}\n        }\n      },\n      \"System.Text.Encoding/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        },\n        \"runtimeTargets\": {\n          \"runtimes/unix/lib/netstandard1.3/System.Text.Encoding.CodePages.dll\": {\n            \"rid\": \"unix\",\n            \"assetType\": \"runtime\",\n            \"assemblyVersion\": \"4.0.2.0\",\n            \"fileVersion\": \"4.6.24705.1\"\n          },\n          \"runtimes/win/lib/netstandard1.3/System.Text.Encoding.CodePages.dll\": {\n            \"rid\": \"win\",\n            \"assetType\": \"runtime\",\n            \"assemblyVersion\": \"4.0.2.0\",\n            \"fileVersion\": \"4.6.24705.1\"\n          }\n        }\n      },\n      \"System.Text.Encoding.Extensions/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encodings.Web/4.5.0\": {\n        \"runtime\": {\n          \"lib/netstandard2.0/System.Text.Encodings.Web.dll\": {\n            \"assemblyVersion\": \"4.0.3.0\",\n            \"fileVersion\": \"4.6.26515.6\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard2.0/System.Text.Encodings.Web.dll\": {}\n        }\n      },\n      \"System.Text.RegularExpressions/4.3.0\": {\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading/4.3.0\": {\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks/4.3.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions/4.5.1\": {},\n      \"System.Threading.Tasks.Parallel/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Thread/4.3.0\": {\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer/4.0.1\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ValueTuple/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.1\"\n        }\n      },\n      \"System.Xml.XDocument/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XmlDocument/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XPath/4.3.0\": {\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XPath.XDocument/4.3.0\": {\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\",\n          \"System.Xml.XPath\": \"4.3.0\"\n        }\n      },\n      \"Telegram.Bot/14.6.0\": {\n        \"dependencies\": {\n          \"NETStandard.Library\": \"2.0.3\",\n          \"Newtonsoft.Json\": \"11.0.2\",\n          \"System.Net.Requests\": \"4.3.0\"\n        },\n        \"runtime\": {\n          \"lib/netstandard1.1/Telegram.Bot.dll\": {\n            \"assemblyVersion\": \"14.6.0.0\",\n            \"fileVersion\": \"14.6.0.0\"\n          }\n        },\n        \"compile\": {\n          \"lib/netstandard1.1/Telegram.Bot.dll\": {}\n        }\n      },\n      \"IntelliTrader.Core/1.0.0\": {\n        \"dependencies\": {\n          \"Autofac\": \"4.8.1\",\n          \"Microsoft.Extensions.Configuration\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.Binder\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.EnvironmentVariables\": \"2.1.1\",\n          \"Microsoft.Extensions.Configuration.Json\": \"2.1.1\",\n          \"Serilog\": \"2.7.1\",\n          \"Serilog.Enrichers.Environment\": \"2.1.2\",\n          \"Serilog.Filters.Expressions\": \"2.0.0\",\n          \"Serilog.Settings.Configuration\": \"2.6.1\",\n          \"Serilog.Sinks.Console\": \"3.1.1\",\n          \"Serilog.Sinks.RollingFile\": \"3.3.0\",\n          \"Telegram.Bot\": \"14.6.0\"\n        },\n        \"runtime\": {\n          \"IntelliTrader.Core.dll\": {}\n        },\n        \"compile\": {\n          \"IntelliTrader.Core.dll\": {}\n        }\n      },\n      \"Microsoft.NETCore.App/2.1.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.DotNetHostPolicy\": \"2.1.0\",\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\",\n          \"Microsoft.NETCore.Targets\": \"2.1.0\",\n          \"NETStandard.Library\": \"2.0.3\"\n        },\n        \"compile\": {\n          \"ref/netcoreapp2.1/Microsoft.CSharp.dll\": {},\n          \"ref/netcoreapp2.1/Microsoft.VisualBasic.dll\": {},\n          \"ref/netcoreapp2.1/Microsoft.Win32.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.AppContext.dll\": {},\n          \"ref/netcoreapp2.1/System.Buffers.dll\": {},\n          \"ref/netcoreapp2.1/System.Collections.Concurrent.dll\": {},\n          \"ref/netcoreapp2.1/System.Collections.Immutable.dll\": {},\n          \"ref/netcoreapp2.1/System.Collections.NonGeneric.dll\": {},\n          \"ref/netcoreapp2.1/System.Collections.Specialized.dll\": {},\n          \"ref/netcoreapp2.1/System.Collections.dll\": {},\n          \"ref/netcoreapp2.1/System.ComponentModel.Annotations.dll\": {},\n          \"ref/netcoreapp2.1/System.ComponentModel.DataAnnotations.dll\": {},\n          \"ref/netcoreapp2.1/System.ComponentModel.EventBasedAsync.dll\": {},\n          \"ref/netcoreapp2.1/System.ComponentModel.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.ComponentModel.TypeConverter.dll\": {},\n          \"ref/netcoreapp2.1/System.ComponentModel.dll\": {},\n          \"ref/netcoreapp2.1/System.Configuration.dll\": {},\n          \"ref/netcoreapp2.1/System.Console.dll\": {},\n          \"ref/netcoreapp2.1/System.Core.dll\": {},\n          \"ref/netcoreapp2.1/System.Data.Common.dll\": {},\n          \"ref/netcoreapp2.1/System.Data.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.Contracts.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.Debug.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.DiagnosticSource.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.FileVersionInfo.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.Process.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.StackTrace.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.TextWriterTraceListener.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.Tools.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.TraceSource.dll\": {},\n          \"ref/netcoreapp2.1/System.Diagnostics.Tracing.dll\": {},\n          \"ref/netcoreapp2.1/System.Drawing.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.Drawing.dll\": {},\n          \"ref/netcoreapp2.1/System.Dynamic.Runtime.dll\": {},\n          \"ref/netcoreapp2.1/System.Globalization.Calendars.dll\": {},\n          \"ref/netcoreapp2.1/System.Globalization.Extensions.dll\": {},\n          \"ref/netcoreapp2.1/System.Globalization.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.Compression.Brotli.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.Compression.FileSystem.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.Compression.ZipFile.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.Compression.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.FileSystem.DriveInfo.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.FileSystem.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.FileSystem.Watcher.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.FileSystem.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.IsolatedStorage.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.MemoryMappedFiles.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.Pipes.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.UnmanagedMemoryStream.dll\": {},\n          \"ref/netcoreapp2.1/System.IO.dll\": {},\n          \"ref/netcoreapp2.1/System.Linq.Expressions.dll\": {},\n          \"ref/netcoreapp2.1/System.Linq.Parallel.dll\": {},\n          \"ref/netcoreapp2.1/System.Linq.Queryable.dll\": {},\n          \"ref/netcoreapp2.1/System.Linq.dll\": {},\n          \"ref/netcoreapp2.1/System.Memory.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.Http.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.HttpListener.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.Mail.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.NameResolution.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.NetworkInformation.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.Ping.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.Requests.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.Security.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.ServicePoint.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.Sockets.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.WebClient.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.WebHeaderCollection.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.WebProxy.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.WebSockets.Client.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.WebSockets.dll\": {},\n          \"ref/netcoreapp2.1/System.Net.dll\": {},\n          \"ref/netcoreapp2.1/System.Numerics.Vectors.dll\": {},\n          \"ref/netcoreapp2.1/System.Numerics.dll\": {},\n          \"ref/netcoreapp2.1/System.ObjectModel.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.DispatchProxy.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.Emit.ILGeneration.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.Emit.Lightweight.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.Emit.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.Extensions.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.Metadata.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.TypeExtensions.dll\": {},\n          \"ref/netcoreapp2.1/System.Reflection.dll\": {},\n          \"ref/netcoreapp2.1/System.Resources.Reader.dll\": {},\n          \"ref/netcoreapp2.1/System.Resources.ResourceManager.dll\": {},\n          \"ref/netcoreapp2.1/System.Resources.Writer.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.CompilerServices.VisualC.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Extensions.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Handles.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.InteropServices.RuntimeInformation.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.InteropServices.WindowsRuntime.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.InteropServices.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Loader.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Numerics.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Serialization.Formatters.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Serialization.Json.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Serialization.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Serialization.Xml.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.Serialization.dll\": {},\n          \"ref/netcoreapp2.1/System.Runtime.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.Claims.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.Cryptography.Algorithms.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.Cryptography.Csp.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.Cryptography.Encoding.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.Cryptography.Primitives.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.Cryptography.X509Certificates.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.Principal.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.SecureString.dll\": {},\n          \"ref/netcoreapp2.1/System.Security.dll\": {},\n          \"ref/netcoreapp2.1/System.ServiceModel.Web.dll\": {},\n          \"ref/netcoreapp2.1/System.ServiceProcess.dll\": {},\n          \"ref/netcoreapp2.1/System.Text.Encoding.Extensions.dll\": {},\n          \"ref/netcoreapp2.1/System.Text.Encoding.dll\": {},\n          \"ref/netcoreapp2.1/System.Text.RegularExpressions.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.Overlapped.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.Tasks.Dataflow.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.Tasks.Extensions.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.Tasks.Parallel.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.Tasks.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.Thread.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.ThreadPool.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.Timer.dll\": {},\n          \"ref/netcoreapp2.1/System.Threading.dll\": {},\n          \"ref/netcoreapp2.1/System.Transactions.Local.dll\": {},\n          \"ref/netcoreapp2.1/System.Transactions.dll\": {},\n          \"ref/netcoreapp2.1/System.ValueTuple.dll\": {},\n          \"ref/netcoreapp2.1/System.Web.HttpUtility.dll\": {},\n          \"ref/netcoreapp2.1/System.Web.dll\": {},\n          \"ref/netcoreapp2.1/System.Windows.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.Linq.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.ReaderWriter.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.Serialization.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.XDocument.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.XPath.XDocument.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.XPath.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.XmlDocument.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.XmlSerializer.dll\": {},\n          \"ref/netcoreapp2.1/System.Xml.dll\": {},\n          \"ref/netcoreapp2.1/System.dll\": {},\n          \"ref/netcoreapp2.1/WindowsBase.dll\": {},\n          \"ref/netcoreapp2.1/mscorlib.dll\": {},\n          \"ref/netcoreapp2.1/netstandard.dll\": {}\n        },\n        \"compileOnly\": true\n      },\n      \"Microsoft.NETCore.DotNetAppHost/2.1.0\": {\n        \"compileOnly\": true\n      },\n      \"Microsoft.NETCore.DotNetHostPolicy/2.1.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.DotNetHostResolver\": \"2.1.0\"\n        },\n        \"compileOnly\": true\n      },\n      \"Microsoft.NETCore.DotNetHostResolver/2.1.0\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.DotNetAppHost\": \"2.1.0\"\n        },\n        \"compileOnly\": true\n      },\n      \"Microsoft.NETCore.Platforms/2.1.0\": {\n        \"compileOnly\": true\n      },\n      \"Microsoft.NETCore.Targets/2.1.0\": {\n        \"compileOnly\": true\n      },\n      \"NETStandard.Library/2.0.3\": {\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.1.0\"\n        },\n        \"compileOnly\": true\n      }\n    }\n  },\n  \"libraries\": {\n    \"IntelliTrader.Web/1.0.0\": {\n      \"type\": \"project\",\n      \"serviceable\": false,\n      \"sha512\": \"\"\n    },\n    \"Autofac/4.8.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-aIT9rupCOdab5RMfxvWTBmOxGU77tLqmvSF4V89SzV6oQcJrtuKw/Xp55xy9EijSktbMka55SbroAPOyT+lziw==\",\n      \"path\": \"autofac/4.8.1\",\n      \"hashPath\": \"autofac.4.8.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Antiforgery/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-BKDp2thf1k3Q2XBSIxC0TvHLvGFOr3ga3DdsxOJNTQ2MEvCuqlNFAoBxXIXWtvP9EHNfLbmKA0+VF7nBqXTlYg==\",\n      \"path\": \"microsoft.aspnetcore.antiforgery/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.antiforgery.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Authentication/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-WgbDLOGoyX0/EoUdAlihMaKIpON6LwCYZ8fiPhZZe+qdCJhvl1aTBmJ/carHcv3NJGT+ETuq2ppYQr7PKLq1CQ==\",\n      \"path\": \"microsoft.aspnetcore.authentication/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.authentication.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Authentication.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-kl1yZmNeUMm9/kWtqoOvIATBavqHPwJICl0FA9rpvNqETqeTgakAbbY25TdG82wKKbjo4LpqZ0YCHwktNPaR2Q==\",\n      \"path\": \"microsoft.aspnetcore.authentication.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.authentication.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Authentication.Cookies/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-Yz9dgcZvZ+OJjJ8ZX/+DtgY0+9ZuKzNO0cHkDUdQubY4W4Ozn5e194s70lNQiiEGJjah9hd/5yuayPAePiz7DQ==\",\n      \"path\": \"microsoft.aspnetcore.authentication.cookies/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.authentication.cookies.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Authentication.Core/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-I7CfHtUAwVH67ayCG9ZrkRI5si0yOlttb0ltMR36dMwXfPR9CYab0o9PyWfTOfGIT9VQ+UgAEH9U9+jVoEjPeg==\",\n      \"path\": \"microsoft.aspnetcore.authentication.core/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.authentication.core.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Authorization/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-z/5haIkI/G2NcCMO288l6l7Jy3BDqzZjHLb2VxjCfj4NKRVv6KlsDD7nGIyAtAbDVKnbOsGBXF6xwhyo4aFGBw==\",\n      \"path\": \"microsoft.aspnetcore.authorization/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.authorization.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Authorization.Policy/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ipuhLj35k90+q6GbBuJaouPDLGwaJilBUUE+y0rtGL+yncCtA1gYFrs3jZ+tRX/zNqlVtlAb1u7wXm5NJ/TkQw==\",\n      \"path\": \"microsoft.aspnetcore.authorization.policy/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.authorization.policy.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Connections.Abstractions/2.1.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-O/dAVpldQyJEzzrY1f6Ki+s4DeAk8qtvcSK8Pk+zJLYB9tZKdWMQ68ob+fy7CsYCdLyhbT/vro0TSBK1teit7g==\",\n      \"path\": \"microsoft.aspnetcore.connections.abstractions/2.1.2\",\n      \"hashPath\": \"microsoft.aspnetcore.connections.abstractions.2.1.2.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Cors/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ajz3/gjo4OYDFId5nJUrBAYJhKW3sJrK5+dLJ3ynTuVyGwY5me3QICukzMeADSKNV+JapSrPKLXIythHwDrQjA==\",\n      \"path\": \"microsoft.aspnetcore.cors/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.cors.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Cryptography.Internal/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-9X49e4ZTv6ipL/Yh1GvVxpgh+ghWMHi+PPE3tQI2HRgG6Jixvmt8LgT/KvAvfgYEDnjsSTRyt/arrHsekHwfMA==\",\n      \"path\": \"microsoft.aspnetcore.cryptography.internal/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.cryptography.internal.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.DataProtection/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-561yQw2Xu5DH05p6uv4G6dD0tfO2KeNuFz/kPREHHFzOk4PF3tdmH9LjCz2fX8eyOvgvfiLSib3atE7thRvZDQ==\",\n      \"path\": \"microsoft.aspnetcore.dataprotection/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.dataprotection.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.DataProtection.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-94UHZlJQUeCeCsrDNrEVDO7nOoFsr1KSetcHAttPA6DDe80XJ57wbWUpzxjoGRimoGG2yS95n7M0bueZCMD7ag==\",\n      \"path\": \"microsoft.aspnetcore.dataprotection.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.dataprotection.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Diagnostics/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-F9GjtKSe4HeOqZJjnnI110wDcvsY0aguALGswbr+R3iuw6X+Mzko7S/Vx7LxQXxInOCJoxnNEkd7Kf59dFFSRg==\",\n      \"path\": \"microsoft.aspnetcore.diagnostics/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.diagnostics.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Diagnostics.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-rLn97UtnaXvD1E8K2UFQg5MBZ/D6KLuMZEEt47qkIIEsEQar84yIlR3HdDDF7ovJ/Bg546EyJXHxXvi7t6G7yw==\",\n      \"path\": \"microsoft.aspnetcore.diagnostics.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.diagnostics.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Hosting/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-rO2JSJGuHJMYE68vm72bFI+PEj1e6zgv9r3izNMEMwyGtjsEDFSHALoGqffnehY63TKqpXdAKElKzPV0UYrMqA==\",\n      \"path\": \"microsoft.aspnetcore.hosting/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.hosting.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Hosting.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-FFZxJAK3sV9JxZ7YP47upycv6VZOcNvJLiLM0FXfvlrb67RC9y4AjCUX1RvI0W1n1v6GMZhWSNb3KYs+O6s26g==\",\n      \"path\": \"microsoft.aspnetcore.hosting.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.hosting.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Hosting.Server.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-xqfxC5t1Jk4ZOQN5xfR2Q0nqTOTN5R6FORk4LqjEzmfX8NDdEsds+Fj6d9bMYqhPWZ4ATRAi8RmaUKYPQuAWbQ==\",\n      \"path\": \"microsoft.aspnetcore.hosting.server.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.hosting.server.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Html.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-tPZG0aA3V8tljooIgKhAiVxu7ZnAnL7QPzz3uxQgs4v7vwwCZTigzh2PIL4QRtezlGFk1jn7PbOtxi+FsmEe0g==\",\n      \"path\": \"microsoft.aspnetcore.html.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.html.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Http/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-u8Fmky/nirrxOU1gBGh97J5gPoniWDc1QiT+J0EFuXJWcFo3BgPGiv7RLvYCi89QpLgIt5CkkPqTkPnWz0eaSA==\",\n      \"path\": \"microsoft.aspnetcore.http/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.http.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Http.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-0TPQgjRy2xJ75GcK18vvrT6/zCtSAWUEBSskSJN/lY0zuvQx2or8lzwr0TdKyMNK8A8MLP4QMLPqL9NOAxe0yg==\",\n      \"path\": \"microsoft.aspnetcore.http.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.http.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Http.Extensions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-0dgKLajNfwElW6fLElwjo+fEyfhXdSN74QeXhOUgPam5UIbU3EBQU/+xD83MnfprAiUPDWHqueTKuB8oa/cjNQ==\",\n      \"path\": \"microsoft.aspnetcore.http.extensions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.http.extensions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Http.Features/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-cMnTXRH+8T7GLht6cXRCMmN1HaYfXti2WEUdXqMUuyJgi4oH9cmzW4nECSBkQjsCs5O06BphyDDDAsTW/zQmpg==\",\n      \"path\": \"microsoft.aspnetcore.http.features/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.http.features.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.JsonPatch/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-T5kx4u+0CH5bD3hB+QEozR4MmLZ7CDGdm0+OD1wxyQBJKNNA6jRSJmbvsZ8nmOEwoGtAfHdXLYM0r3/Zw6J4JQ==\",\n      \"path\": \"microsoft.aspnetcore.jsonpatch/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.jsonpatch.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Localization/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-oy13Ppp0iBLHAzq03R5tEBNTAfatboreqW7YEMhVA2fu6L0KLmBk3njHc0FJaFnwZwCbmPnRtr81J8A7NWqQuQ==\",\n      \"path\": \"microsoft.aspnetcore.localization/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.localization.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-3mHitdj9MClvbFThDsVhojGH2PxWWxhJNFzFwNnofSdORrnRby9bikM+HCqUOz2gvxnyYz5jsgbA88+CGkNy4A==\",\n      \"path\": \"microsoft.aspnetcore.mvc/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-/XgeeXi0LrykMlMCNMQftj2XyEua4JT5AFAt3D3xE6KChx0PydXTFiwQtDvbGpNvarPQWWdyEfq1rKlgyVGlXA==\",\n      \"path\": \"microsoft.aspnetcore.mvc.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.ApiExplorer/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-GGPbYZfzJvu6rigtCN0FRQD4B8ERmMO+grCyf/lfQhmqK9cTfhDcU8Zfw75SXrQ3Ity1lSvYpf26XeFVIi5Y5A==\",\n      \"path\": \"microsoft.aspnetcore.mvc.apiexplorer/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.apiexplorer.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.Core/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-QoYLsJHrN7LNnL1LWzSGzQm3v/1ERI5csb4LSzNYm71EcCG8SWckw76GgXNx6mjsJXfxsvoqRAovnLQKCCBtvA==\",\n      \"path\": \"microsoft.aspnetcore.mvc.core/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.core.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.Cors/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-a04jcvPbG6IfaugJe3CS59ZhSRAVLmwVEGDLp4wGuR4/9yW3T4mCZgqcSQz+5921j/hRGn1Jwu/b05bWkg+wBg==\",\n      \"path\": \"microsoft.aspnetcore.mvc.cors/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.cors.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.DataAnnotations/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-3/LdPk7u3VitfUxVu+forzb+YFa/G4tqFDQKG20mMHrAnE7ranDUhqURD7qoy8JFLRWdhvvdBhUJaATfvvmTVg==\",\n      \"path\": \"microsoft.aspnetcore.mvc.dataannotations/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.dataannotations.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.Formatters.Json/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-nBzpBR0Ei/4L63+ylGS6P4gP+u+/S1cIvUU4+G+4Rk+vtzNT5KsoFP9TfCvW8hGQ6ShehjT7wXMuci/D2SbCQQ==\",\n      \"path\": \"microsoft.aspnetcore.mvc.formatters.json/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.formatters.json.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.Localization/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-JN/d/T8JUYoF/YMBupIu92ZcP9PcYfLLQqIZWvfyJrNNftgXENAHMLn1999POEzG44RjGouWdioSH8QZJ1mTTQ==\",\n      \"path\": \"microsoft.aspnetcore.mvc.localization/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.localization.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.Razor/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-9WCfQX8+xZN8pzRK8ZxCJw/3lpsKsg3iQvFr6CRz4UtayLEoq/uzLKL5xvY8fj1rVJjt3wBh+YBhheB/196QSg==\",\n      \"path\": \"microsoft.aspnetcore.mvc.razor/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.razor.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.Razor.Extensions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-w/4GAxZS5y9CnlIO4z04sC7I+cLVVYsvI+hC+Thh2vy5AQxNZj9ZIxmdIPtvqQfZ2JdURQ7cpBsr8pzf4YhTEA==\",\n      \"path\": \"microsoft.aspnetcore.mvc.razor.extensions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.razor.extensions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.RazorPages/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-icxhGYO1z5IQsrmJhbIJUHM2a0mTK7g1kdPR/mnB5L4r35im8ElX0449AFN3KlA0C00E6mzXVe1CCJ3wO+TUxQ==\",\n      \"path\": \"microsoft.aspnetcore.mvc.razorpages/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.razorpages.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.TagHelpers/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-wLHZ9TUdD9Gl2rVihrNGmRJ1LGTjiRzPM4d78efClOpFJwhMaHCnr9ktfQhnJX4XQj0w22XvPPCV0GxSrVp4Lg==\",\n      \"path\": \"microsoft.aspnetcore.mvc.taghelpers/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.taghelpers.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Mvc.ViewFeatures/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-4F4uu3Hh5pgQ/2XkKgG2XEfPIvzUUjpOrSPIdOpMzxloTfYM/jK6xEW6kM9DE5vYhyW9EE02sngRBh8cmU0vng==\",\n      \"path\": \"microsoft.aspnetcore.mvc.viewfeatures/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.mvc.viewfeatures.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Razor/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-oDxJTufrOF2Y7g+p2jU5+2xtrcsb3KX20pH/KosLW5rbsJMAqaOwprI6gJlBQCGtMCYl/MbnC45ZObPmzyI0NQ==\",\n      \"path\": \"microsoft.aspnetcore.razor/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.razor.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Razor.Design/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-f8PKGcxiezL1RVqmnmrazj24Mj4KCTSXqwdotl7Lc+82h8iLV7ItxEIShTJakG7M9iw0ZuCocM0J/IhYesdQrg==\",\n      \"path\": \"microsoft.aspnetcore.razor.design/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.razor.design.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Razor.Language/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-5HX7/SguN9F8cdJ6GBBFJauEii/k6XPuI1gHucOcOBKKetgm4nG/xrHzRGSBTxmc1rbCcVKrBl10/PYItE7JyA==\",\n      \"path\": \"microsoft.aspnetcore.razor.language/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.razor.language.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Razor.Runtime/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-dGublvci7Lwu8gAegh81YXATyKGupWHb5RDHPsIO/Ct++xG7Lv9/6nNbci05sqYienZgprDbTAH8G7PmBCpIKQ==\",\n      \"path\": \"microsoft.aspnetcore.razor.runtime/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.razor.runtime.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.ResponseCaching.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-F2/eDBTwGdTdQ+YPrlf7DBprzbHVZmZqnCTkHT6Jge7MQDu0xgUmDfNyBUzg9jn38RSKnDp6RWLQSJ6yqsYdIQ==\",\n      \"path\": \"microsoft.aspnetcore.responsecaching.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.responsecaching.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Routing/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-BnVEKMGIkRcZecG3zR+tl9tYGkViz1k/WzYVNRfdaAN0LeuSabNP0NlG037oz+pDPsLzzNkFeLSOh/w0AKLaig==\",\n      \"path\": \"microsoft.aspnetcore.routing/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.routing.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Routing.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-+Yxsy/ZcCthcziktuhfC6WpQ/cZzgD/IsQ96xefNKrCzIm9jXjfNK3ONsoScvyFFihNohp7zAVPiic5J6CvUDw==\",\n      \"path\": \"microsoft.aspnetcore.routing.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.routing.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Server.Kestrel/2.1.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ol4rQS7QpGBtfcHY39ecVnAg1Fe0MXTIZN/hm4ldJAXE2+PmH7BnLl1yJrZB5MMeXHYsU+v1N0+iSkX5jJm2uw==\",\n      \"path\": \"microsoft.aspnetcore.server.kestrel/2.1.2\",\n      \"hashPath\": \"microsoft.aspnetcore.server.kestrel.2.1.2.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Server.Kestrel.Core/2.1.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-tig0vQgH1hvY3We458hCRBjJfu0zc+sqGZxTboXde8pT932MdsxxUzimxPCHdg3RBRRzVct1E1KtMb6c6M5Pxw==\",\n      \"path\": \"microsoft.aspnetcore.server.kestrel.core/2.1.2\",\n      \"hashPath\": \"microsoft.aspnetcore.server.kestrel.core.2.1.2.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Server.Kestrel.Https/2.1.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-Z+GhgasOk1tr3g5NVkUmVqHpbi+ORqkTenErnBTF1DjgXileomxweijL2StNvmv1dtJhxhclZpIGePpWIKAAfA==\",\n      \"path\": \"microsoft.aspnetcore.server.kestrel.https/2.1.2\",\n      \"hashPath\": \"microsoft.aspnetcore.server.kestrel.https.2.1.2.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.1.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-wtlxVAdrp6BsBi3/A3yeIOygZl1m2Wn2GOYBjngp5tTbhMJZm0bVO+xPcw2bOKUL1eQHUQwRsJB5kabPKphotg==\",\n      \"path\": \"microsoft.aspnetcore.server.kestrel.transport.abstractions/2.1.2\",\n      \"hashPath\": \"microsoft.aspnetcore.server.kestrel.transport.abstractions.2.1.2.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.1.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-PHCpMsg8aSuNFkgXX9f+HIkYNpWMDj5l+hFni4Zyuv3hv2xfRdehqvEZTAsYPRbRWy5hyewCSZ3Kh/fifLQc7g==\",\n      \"path\": \"microsoft.aspnetcore.server.kestrel.transport.sockets/2.1.2\",\n      \"hashPath\": \"microsoft.aspnetcore.server.kestrel.transport.sockets.2.1.2.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.StaticFiles/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-3xumS58evfsC4cd8OXtYRafbwuVk5c37dsGQ1E1m0wZvRVUXScRWkTGdcPJcijoImlhoQK2pj6sY7NFMc5PfbQ==\",\n      \"path\": \"microsoft.aspnetcore.staticfiles/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.staticfiles.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.AspNetCore.WebUtilities/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-gvCdObgQDLdZ9enyFQuPb3Rae6QyzZAPgHiv5JhYjORLMW1UNgWXvdqLov6iGtnyG+BBCavPooW9ScWGQCJHLg==\",\n      \"path\": \"microsoft.aspnetcore.webutilities/2.1.1\",\n      \"hashPath\": \"microsoft.aspnetcore.webutilities.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.CodeAnalysis.Analyzers/1.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-6csv1zVOCb3tncoRbuYclgO5qksGIypQbUb3pofrcWVibbT3Bpq0rx19Xf5Vm1l1MzmY2HJiUY1JubL0YZvFNA==\",\n      \"path\": \"microsoft.codeanalysis.analyzers/1.1.0\",\n      \"hashPath\": \"microsoft.codeanalysis.analyzers.1.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.CodeAnalysis.Common/2.8.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-lYUBqh3OD3iEQqxt9KB472VzgOnEKoUVG4Lx5Xw4oJe9dZtITkHFtct+T73jH3FOASFI1NSzzP5MBM0c9zZspA==\",\n      \"path\": \"microsoft.codeanalysis.common/2.8.0\",\n      \"hashPath\": \"microsoft.codeanalysis.common.2.8.0.nupkg.sha512\"\n    },\n    \"Microsoft.CodeAnalysis.CSharp/2.8.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-+4CHAwHMwLO5GRqPJ7Khv2Ny//omhukPKP3Ny/d2XDpt11bX35zb9pTziwZN0eNvxj6a46joIdHEYQ1JsekI3w==\",\n      \"path\": \"microsoft.codeanalysis.csharp/2.8.0\",\n      \"hashPath\": \"microsoft.codeanalysis.csharp.2.8.0.nupkg.sha512\"\n    },\n    \"Microsoft.CodeAnalysis.Razor/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-D5zUSmQHsgKosYlWLQjs6uXn4n7llEdUwFhJz7EIwR16ge18q8p8BJ547out9ScnMDuwHA8MeCPe8WMwCaFAPw==\",\n      \"path\": \"microsoft.codeanalysis.razor/2.1.1\",\n      \"hashPath\": \"microsoft.codeanalysis.razor.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.CSharp/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-EGoBmf3Na2ppbhPePDE9PlX81r1HuOZH5twBrq7couJZiPTjUnD3648balerQJO6EJ8Sj+43+XuRwQ7r+3tE3w==\",\n      \"path\": \"microsoft.csharp/4.5.0\",\n      \"hashPath\": \"microsoft.csharp.4.5.0.nupkg.sha512\"\n    },\n    \"Microsoft.DotNet.PlatformAbstractions/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-wkCXkBS0q+5hsbeikjfsHCGP3nNe1L1MrDEBPCBKm+4UH8nXqHLxDZuBrTYaVY85CGIx2y1qW90nO6b+ORAfrA==\",\n      \"path\": \"microsoft.dotnet.platformabstractions/2.1.0\",\n      \"hashPath\": \"microsoft.dotnet.platformabstractions.2.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Caching.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-gcPRTtchou4pIEdLYhh9xoBDjwCaCLiTHJaFN2IWJCP+TGJcIHQYblPMftw6fajHER9ZrvPO5RYZUyLmH1eNIA==\",\n      \"path\": \"microsoft.extensions.caching.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.caching.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Caching.Memory/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-KV2w9nelcxgl1Y028qmexCcgBK+CtZ18fE2eIypB1lUtLOGBrzP+XhcJTxBYwXPnYPkxazqdzcOfIRxz/Bq2uQ==\",\n      \"path\": \"microsoft.extensions.caching.memory/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.caching.memory.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Configuration/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-1JaydycXzbfAExlsD7XIWykzVnU/wZM86KzrHyGlXuxqnqzcWSXLJn4Ejn8bDnq07CEJNZ+GjsxWKlJ8kFfnvQ==\",\n      \"path\": \"microsoft.extensions.configuration/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.configuration.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Configuration.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-9EMhOWU2eOQOtMIJ+vfwKJpnLRc1Wl3vXu8qXeevA91cSY4j3WvArmF7ApGtJwa7yKewJTvlQlBSn9OSnLFg6Q==\",\n      \"path\": \"microsoft.extensions.configuration.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.configuration.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Configuration.Binder/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-t7KFAv6AxyUsZj9QN8FAbusg+X5baCELl+XtscyuP1IGUv5UctyY7/rNZLyiKaV7HhAcDQ1zC5ZQNQQFn6JpAA==\",\n      \"path\": \"microsoft.extensions.configuration.binder/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.configuration.binder.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Configuration.EnvironmentVariables/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-rDFRChBvs6sPGC+JjshKsP4kWRvsG8Y9MQKduDu60RWnJpFiIpQ7HK2K9sPrCL1MaYEk894PUkiZ5Xdsm9cPvg==\",\n      \"path\": \"microsoft.extensions.configuration.environmentvariables/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.configuration.environmentvariables.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Configuration.FileExtensions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-JnhKotPCs1+X4CPSsHOk8CpxmBeIS/vIXYewsoM8XflXNhpzMe1gfIckQyuRKyORlGaNFEBr4WrPjpZ159bS/Q==\",\n      \"path\": \"microsoft.extensions.configuration.fileextensions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.configuration.fileextensions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Configuration.Json/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-f6KcI9v0GVA4YL/ExoxrEfeQv9La3hyQnySfgxGkFtMeDJIUun0ANoMjspbdpXXnuaScwgbQ2mFE3lJHt9lpJw==\",\n      \"path\": \"microsoft.extensions.configuration.json/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.configuration.json.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.DependencyInjection/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-2nshYaLTn73Ie+/yTkb7EZIXwQeFIXsYCBy/jSY9bMayYykGNjdWa25frayhuPAGVbZpEgfgp3d4JRVEuVyEqQ==\",\n      \"path\": \"microsoft.extensions.dependencyinjection/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.dependencyinjection.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-PW1596sF97gpIc1JuUuYvTmeLfeqC5whbWPsWgJhN0fdwz683him3b/HB0dqhFesVssOjnnA0fEz4+S0gUeBqA==\",\n      \"path\": \"microsoft.extensions.dependencyinjection.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.dependencyinjection.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.DependencyModel/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-3KPT6CLH0VEGr2um9aG1rYTmqfMVlkRuueFpN6AxeIKpcMA4OVHf4aNpgYXZ6oF+x4uh9VhK/66FgPCd1mMlnQ==\",\n      \"path\": \"microsoft.extensions.dependencymodel/2.1.0\",\n      \"hashPath\": \"microsoft.extensions.dependencymodel.2.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.FileProviders.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-qOJP+VAlXDeMQSJ6iflW62bEsN3S1NJIPHmhKFA9L37yU+jce2wbwesA7sDe9WdJ8+SoKtLnHPUxvOyQrAcRCA==\",\n      \"path\": \"microsoft.extensions.fileproviders.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.fileproviders.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.FileProviders.Composite/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-SovLUACJ3C+iRlHo4VdZw0IDX+v7+32paTJf7v5ZyzyWqijUkDYXr81gL7tkCfCkJmBYnrc6bScoj2Eaxlrudw==\",\n      \"path\": \"microsoft.extensions.fileproviders.composite/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.fileproviders.composite.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.FileProviders.Physical/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-pbT/J3B686Xgktv5WH11FbcbZXDmBQuCN3ce8IKIF+DpOk3p0RgUPrOXcYNp81TyH+K/5Cosr4VFVjYMoirNDg==\",\n      \"path\": \"microsoft.extensions.fileproviders.physical/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.fileproviders.physical.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.FileSystemGlobbing/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-Pu/O8jBc7QlEmqmbDGVosuDlyzGspMuKc71rOsJigwGMF5574aWYw9uRMX+ho1dmbnL502ZYHo6PlBP3IXkm5A==\",\n      \"path\": \"microsoft.extensions.filesystemglobbing/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.filesystemglobbing.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Hosting.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-v7mPlJ68Dsev9gn6w5tJJZI798r6gCmwKBv0pwJ5PunLEITYjrv1+QJ/wYkp7KuRcr8VRUML8mJg/mgUjgHggA==\",\n      \"path\": \"microsoft.extensions.hosting.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.hosting.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Localization/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-XPVATgcnzWwo6NYXsZfiEBSSFWWOEdFMn099BIlJCgwVSTLdZD130xRFH4wGXg5sMos3xXsBLv1fffQ67Ju+qg==\",\n      \"path\": \"microsoft.extensions.localization/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.localization.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Localization.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-V1znqxUEDHAfnCDXLsfrbY+RmtrFkJqOFhVBOIrcqQMp6MFJvIV9QpDTMq8JzqYc++aAraIoUEAsAwoa8otlOw==\",\n      \"path\": \"microsoft.extensions.localization.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.localization.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Logging/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-x4/RzeReQSIi4nVpOjXEySm/xUSr6lBjuecdYnlUboWxbLSm2j3vhFV5OLGRp3gfte3cRMdysMNa/wyZN0t/Tw==\",\n      \"path\": \"microsoft.extensions.logging/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.logging.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Logging.Abstractions/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-QWFWKrdeoDSEr8nVJaBAVDMj24wnh9clGzDNmMdgHHRsOIwTUMeh4XljeZXJhIKPT00jWuzwEzn3uNxOtO4cYg==\",\n      \"path\": \"microsoft.extensions.logging.abstractions/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.logging.abstractions.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.ObjectPool/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-FE4JmV6FEZdmqSKqvld5TRnvHfJfrw9QzvvZlAiTn+FCiq/1ZaQDpcYBRH7dMHFWIsYD6Z2UTsufdbCGznox8g==\",\n      \"path\": \"microsoft.extensions.objectpool/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.objectpool.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Options/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-j0zOfTt1Qm+JDW2m+6Q/aj1m4C8+onudUu4ls/fN69VxruZkMWmX1bPKkbkYIPNNxJsf4k7FOkVq5o1vEFq9pQ==\",\n      \"path\": \"microsoft.extensions.options/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.options.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.Primitives/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-Svz25/egj1TsNL4118jyMqkhDiu0l8QYWq2p52P4BBN0GbqwR18ZRIctSP5TTDJy0m0EFC8aB2FOVjGtvEGWSA==\",\n      \"path\": \"microsoft.extensions.primitives/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.primitives.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Extensions.WebEncoders/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-0fR5UV3qREnTpGiqUkz6p30gHzRNvZExgTpch0Gwc+lVUh7D2MBLK/2ohmsMnXp7ckYiEAHhEb9Z/NTUdajKXA==\",\n      \"path\": \"microsoft.extensions.webencoders/2.1.1\",\n      \"hashPath\": \"microsoft.extensions.webencoders.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Net.Http.Headers/2.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-tNh1YCfZ943/d3WSE6cD57O05rhvi3lmKgwoi3zFg4wc/O/oec5FNHZmBCRau4GfzRC5zS/CBdOAkRwbvtZSaQ==\",\n      \"path\": \"microsoft.net.http.headers/2.1.1\",\n      \"hashPath\": \"microsoft.net.http.headers.2.1.1.nupkg.sha512\"\n    },\n    \"Microsoft.Win32.Registry/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-vduxuHEqRgRrTE8wYG8Wxj/+6wwzddOmZzjKZx6rFMc/91aUBxI5etAFYxesoNaIja5NpgSTcnk6cN8BeYXf9A==\",\n      \"path\": \"microsoft.win32.registry/4.5.0\",\n      \"hashPath\": \"microsoft.win32.registry.4.5.0.nupkg.sha512\"\n    },\n    \"Newtonsoft.Json/11.0.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ==\",\n      \"path\": \"newtonsoft.json/11.0.2\",\n      \"hashPath\": \"newtonsoft.json.11.0.2.nupkg.sha512\"\n    },\n    \"Newtonsoft.Json.Bson/1.0.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==\",\n      \"path\": \"newtonsoft.json.bson/1.0.1\",\n      \"hashPath\": \"newtonsoft.json.bson.1.0.1.nupkg.sha512\"\n    },\n    \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\",\n      \"path\": \"runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\",\n      \"path\": \"runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\",\n      \"path\": \"runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.native.System/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n      \"path\": \"runtime.native.system/4.3.0\",\n      \"hashPath\": \"runtime.native.system.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.native.System.IO.Compression/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n      \"path\": \"runtime.native.system.io.compression/4.3.0\",\n      \"hashPath\": \"runtime.native.system.io.compression.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.native.System.Net.Http/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n      \"path\": \"runtime.native.system.net.http/4.3.0\",\n      \"hashPath\": \"runtime.native.system.net.http.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.native.System.Security.Cryptography.Apple/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n      \"path\": \"runtime.native.system.security.cryptography.apple/4.3.0\",\n      \"hashPath\": \"runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n      \"path\": \"runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\",\n      \"path\": \"runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\",\n      \"path\": \"runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\",\n      \"path\": \"runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.0\",\n      \"hashPath\": \"runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\",\n      \"path\": \"runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\",\n      \"path\": \"runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\",\n      \"path\": \"runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\",\n      \"path\": \"runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\",\n      \"path\": \"runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"Serilog/2.7.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-5eS6AD4hesOYvlwYqjntLaOonDnEiY22QiMUKueJHPrm5vm1k4tzXgOgCb2mJoMnj6l5Wt6AXV5liXzXymvarg==\",\n      \"path\": \"serilog/2.7.1\",\n      \"hashPath\": \"serilog.2.7.1.nupkg.sha512\"\n    },\n    \"Serilog.Enrichers.Environment/2.1.2\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-Uj3X4tGW8T38IIGCp/MxbHgg5vG3HN5myBQLIw2JTt87Gwv11NgZJGc4hunmFocQYny09CPVy2LRfiI6gAqQ4Q==\",\n      \"path\": \"serilog.enrichers.environment/2.1.2\",\n      \"hashPath\": \"serilog.enrichers.environment.2.1.2.nupkg.sha512\"\n    },\n    \"Serilog.Filters.Expressions/2.0.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-JhD2uV1s3ixF4L2dSB7t7jV5OKG8AEQOYtfTqSVkNm9X/g6zr1uoGH62XhDfCzEbyd5fiB9Rv4IXm+8m98Ao9Q==\",\n      \"path\": \"serilog.filters.expressions/2.0.0\",\n      \"hashPath\": \"serilog.filters.expressions.2.0.0.nupkg.sha512\"\n    },\n    \"Serilog.Settings.Configuration/2.6.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-d23bkPRrI/lxfe3FQ2C1KouU/3tBDhGhIHvmlW94rDeNductsu6wzEOGTI9neImD3AwzCB/WPq2BJCZAWe2R4Q==\",\n      \"path\": \"serilog.settings.configuration/2.6.1\",\n      \"hashPath\": \"serilog.settings.configuration.2.6.1.nupkg.sha512\"\n    },\n    \"Serilog.Sinks.Console/3.1.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==\",\n      \"path\": \"serilog.sinks.console/3.1.1\",\n      \"hashPath\": \"serilog.sinks.console.3.1.1.nupkg.sha512\"\n    },\n    \"Serilog.Sinks.File/3.2.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-VHbo68pMg5hwSWrzLEdZv5b/rYmIgHIRhd4d5rl8GnC5/a8Fr+RShT5kWyeJOXax1el6mNJ+dmHDOVgnNUQxaw==\",\n      \"path\": \"serilog.sinks.file/3.2.0\",\n      \"hashPath\": \"serilog.sinks.file.3.2.0.nupkg.sha512\"\n    },\n    \"Serilog.Sinks.RollingFile/3.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-2lT5X1r3GH4P0bRWJfhA7etGl8Q2Ipw9AACvtAHWRUSpYZ42NGVyHoVs2ALBZ/cAkkS+tA4jl80Zie144eLQPg==\",\n      \"path\": \"serilog.sinks.rollingfile/3.3.0\",\n      \"hashPath\": \"serilog.sinks.rollingfile.3.3.0.nupkg.sha512\"\n    },\n    \"Superpower/2.0.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-LvsFRxO7VJuUXiaIr3agLuTQTCVYtzgmGJ4BiNe4/DKplOMZ4pibpVSg1ON5zXdCci1jYNfRgQlNEKv0d0Kcog==\",\n      \"path\": \"superpower/2.0.0\",\n      \"hashPath\": \"superpower.2.0.0.nupkg.sha512\"\n    },\n    \"System.AppContext/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n      \"path\": \"system.appcontext/4.3.0\",\n      \"hashPath\": \"system.appcontext.4.3.0.nupkg.sha512\"\n    },\n    \"System.Buffers/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-xpHYjjtyTEpzMwtSQBWdVc3dPjLdQtvyUg6fBlBqcLl1r2Y7gDG/W/enAYOB98nG3oD3Q153Y2FBO8JDWd+0Xw==\",\n      \"path\": \"system.buffers/4.5.0\",\n      \"hashPath\": \"system.buffers.4.5.0.nupkg.sha512\"\n    },\n    \"System.Collections/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n      \"path\": \"system.collections/4.3.0\",\n      \"hashPath\": \"system.collections.4.3.0.nupkg.sha512\"\n    },\n    \"System.Collections.Concurrent/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n      \"path\": \"system.collections.concurrent/4.3.0\",\n      \"hashPath\": \"system.collections.concurrent.4.3.0.nupkg.sha512\"\n    },\n    \"System.Collections.Immutable/1.3.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-n+AGX7zmiZumW9aggOkXaHzUeAS3EfeTErnkKCusyONUozbTv+kMb8VE36m+ldV6kF9g57G2c641KCdgH9E0pg==\",\n      \"path\": \"system.collections.immutable/1.3.1\",\n      \"hashPath\": \"system.collections.immutable.1.3.1.nupkg.sha512\"\n    },\n    \"System.Collections.NonGeneric/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==\",\n      \"path\": \"system.collections.nongeneric/4.3.0\",\n      \"hashPath\": \"system.collections.nongeneric.4.3.0.nupkg.sha512\"\n    },\n    \"System.ComponentModel/4.0.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==\",\n      \"path\": \"system.componentmodel/4.0.1\",\n      \"hashPath\": \"system.componentmodel.4.0.1.nupkg.sha512\"\n    },\n    \"System.ComponentModel.Annotations/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-IjDa643EO77A4CL9dhxfZ6zzGu+pM8Ar0NYPRMN3TvDiga4uGDzFHOj/ArpyNxxKyO5IFT2LZ0rK3kUog7g3jA==\",\n      \"path\": \"system.componentmodel.annotations/4.5.0\",\n      \"hashPath\": \"system.componentmodel.annotations.4.5.0.nupkg.sha512\"\n    },\n    \"System.Console/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n      \"path\": \"system.console/4.3.0\",\n      \"hashPath\": \"system.console.4.3.0.nupkg.sha512\"\n    },\n    \"System.Diagnostics.Debug/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n      \"path\": \"system.diagnostics.debug/4.3.0\",\n      \"hashPath\": \"system.diagnostics.debug.4.3.0.nupkg.sha512\"\n    },\n    \"System.Diagnostics.DiagnosticSource/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-UumL3CJklk5WyEt0eImPmjeuyY1JgJ7Thmg2hAeZGKCv+9iuuAsoc2wcXjypdo3J8VNEmVCH2Bgn/kIw8NI2bA==\",\n      \"path\": \"system.diagnostics.diagnosticsource/4.5.0\",\n      \"hashPath\": \"system.diagnostics.diagnosticsource.4.5.0.nupkg.sha512\"\n    },\n    \"System.Diagnostics.FileVersionInfo/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-omCF64wzQ3Q2CeIqkD6lmmxeMZtGHUmzgFMPjfVaOsyqpR66p/JaZzManMw1s33osoAb5gqpncsjie67+yUPHQ==\",\n      \"path\": \"system.diagnostics.fileversioninfo/4.3.0\",\n      \"hashPath\": \"system.diagnostics.fileversioninfo.4.3.0.nupkg.sha512\"\n    },\n    \"System.Diagnostics.StackTrace/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-BiHg0vgtd35/DM9jvtaC1eKRpWZxr0gcQd643ABG7GnvSlf5pOkY2uyd42mMOJoOmKvnpNj0F4tuoS1pacTwYw==\",\n      \"path\": \"system.diagnostics.stacktrace/4.3.0\",\n      \"hashPath\": \"system.diagnostics.stacktrace.4.3.0.nupkg.sha512\"\n    },\n    \"System.Diagnostics.Tools/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n      \"path\": \"system.diagnostics.tools/4.3.0\",\n      \"hashPath\": \"system.diagnostics.tools.4.3.0.nupkg.sha512\"\n    },\n    \"System.Diagnostics.Tracing/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n      \"path\": \"system.diagnostics.tracing/4.3.0\",\n      \"hashPath\": \"system.diagnostics.tracing.4.3.0.nupkg.sha512\"\n    },\n    \"System.Dynamic.Runtime/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==\",\n      \"path\": \"system.dynamic.runtime/4.3.0\",\n      \"hashPath\": \"system.dynamic.runtime.4.3.0.nupkg.sha512\"\n    },\n    \"System.Globalization/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n      \"path\": \"system.globalization/4.3.0\",\n      \"hashPath\": \"system.globalization.4.3.0.nupkg.sha512\"\n    },\n    \"System.Globalization.Calendars/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n      \"path\": \"system.globalization.calendars/4.3.0\",\n      \"hashPath\": \"system.globalization.calendars.4.3.0.nupkg.sha512\"\n    },\n    \"System.Globalization.Extensions/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n      \"path\": \"system.globalization.extensions/4.3.0\",\n      \"hashPath\": \"system.globalization.extensions.4.3.0.nupkg.sha512\"\n    },\n    \"System.IO/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n      \"path\": \"system.io/4.3.0\",\n      \"hashPath\": \"system.io.4.3.0.nupkg.sha512\"\n    },\n    \"System.IO.Compression/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n      \"path\": \"system.io.compression/4.3.0\",\n      \"hashPath\": \"system.io.compression.4.3.0.nupkg.sha512\"\n    },\n    \"System.IO.FileSystem/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n      \"path\": \"system.io.filesystem/4.3.0\",\n      \"hashPath\": \"system.io.filesystem.4.3.0.nupkg.sha512\"\n    },\n    \"System.IO.FileSystem.Primitives/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n      \"path\": \"system.io.filesystem.primitives/4.3.0\",\n      \"hashPath\": \"system.io.filesystem.primitives.4.3.0.nupkg.sha512\"\n    },\n    \"System.IO.Pipelines/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-Kq9eZWVKN9khHhkatLWLLxYCs3j9qSNMZELqn2YG1YsCMv6bPmAtaN0CfA6l7vxFbiV02C996Dy7yHO8DkaJLg==\",\n      \"path\": \"system.io.pipelines/4.5.0\",\n      \"hashPath\": \"system.io.pipelines.4.5.0.nupkg.sha512\"\n    },\n    \"System.Linq/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n      \"path\": \"system.linq/4.3.0\",\n      \"hashPath\": \"system.linq.4.3.0.nupkg.sha512\"\n    },\n    \"System.Linq.Expressions/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n      \"path\": \"system.linq.expressions/4.3.0\",\n      \"hashPath\": \"system.linq.expressions.4.3.0.nupkg.sha512\"\n    },\n    \"System.Memory/4.5.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-vcG3/MbfpxznMkkkaAblJi7RHOmuP7kawQMhDgLSuA1tRpRQYsFSCTxRSINDUgn2QNn2jWeLxv8er5BXbyACkw==\",\n      \"path\": \"system.memory/4.5.1\",\n      \"hashPath\": \"system.memory.4.5.1.nupkg.sha512\"\n    },\n    \"System.Net.Http/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n      \"path\": \"system.net.http/4.3.0\",\n      \"hashPath\": \"system.net.http.4.3.0.nupkg.sha512\"\n    },\n    \"System.Net.Primitives/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n      \"path\": \"system.net.primitives/4.3.0\",\n      \"hashPath\": \"system.net.primitives.4.3.0.nupkg.sha512\"\n    },\n    \"System.Net.Requests/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-OZNUuAs0kDXUzm7U5NZ1ojVta5YFZmgT2yxBqsQ7Eseq5Ahz88LInGRuNLJ/NP2F8W1q7tse1pKDthj3reF5QA==\",\n      \"path\": \"system.net.requests/4.3.0\",\n      \"hashPath\": \"system.net.requests.4.3.0.nupkg.sha512\"\n    },\n    \"System.Net.WebHeaderCollection/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-XZrXYG3c7QV/GpWeoaRC02rM6LH2JJetfVYskf35wdC/w2fFDFMphec4gmVH2dkll6abtW14u9Rt96pxd9YH2A==\",\n      \"path\": \"system.net.webheadercollection/4.3.0\",\n      \"hashPath\": \"system.net.webheadercollection.4.3.0.nupkg.sha512\"\n    },\n    \"System.Numerics.Vectors/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-MNcaYxUJvUcoXOa+jgKl/GDw/Mh+wMrxDjW4dre7qrp35LUGTjUBNtZsNjxsWX592ocdyqt1X5hMJB+5OStoYw==\",\n      \"path\": \"system.numerics.vectors/4.5.0\",\n      \"hashPath\": \"system.numerics.vectors.4.5.0.nupkg.sha512\"\n    },\n    \"System.ObjectModel/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n      \"path\": \"system.objectmodel/4.3.0\",\n      \"hashPath\": \"system.objectmodel.4.3.0.nupkg.sha512\"\n    },\n    \"System.Reflection/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n      \"path\": \"system.reflection/4.3.0\",\n      \"hashPath\": \"system.reflection.4.3.0.nupkg.sha512\"\n    },\n    \"System.Reflection.Emit/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n      \"path\": \"system.reflection.emit/4.3.0\",\n      \"hashPath\": \"system.reflection.emit.4.3.0.nupkg.sha512\"\n    },\n    \"System.Reflection.Emit.ILGeneration/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n      \"path\": \"system.reflection.emit.ilgeneration/4.3.0\",\n      \"hashPath\": \"system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512\"\n    },\n    \"System.Reflection.Emit.Lightweight/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n      \"path\": \"system.reflection.emit.lightweight/4.3.0\",\n      \"hashPath\": \"system.reflection.emit.lightweight.4.3.0.nupkg.sha512\"\n    },\n    \"System.Reflection.Extensions/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n      \"path\": \"system.reflection.extensions/4.3.0\",\n      \"hashPath\": \"system.reflection.extensions.4.3.0.nupkg.sha512\"\n    },\n    \"System.Reflection.Metadata/1.6.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-I4aWCii7N1bmn43vviRfJQYW6UAco1G/CcjJouvgGdb/sr2BRTSnddhaPMg2oxu9VHFn8T1z3dTLq0pna8zmtA==\",\n      \"path\": \"system.reflection.metadata/1.6.0\",\n      \"hashPath\": \"system.reflection.metadata.1.6.0.nupkg.sha512\"\n    },\n    \"System.Reflection.Primitives/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n      \"path\": \"system.reflection.primitives/4.3.0\",\n      \"hashPath\": \"system.reflection.primitives.4.3.0.nupkg.sha512\"\n    },\n    \"System.Reflection.TypeExtensions/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n      \"path\": \"system.reflection.typeextensions/4.3.0\",\n      \"hashPath\": \"system.reflection.typeextensions.4.3.0.nupkg.sha512\"\n    },\n    \"System.Resources.ResourceManager/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n      \"path\": \"system.resources.resourcemanager/4.3.0\",\n      \"hashPath\": \"system.resources.resourcemanager.4.3.0.nupkg.sha512\"\n    },\n    \"System.Runtime/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n      \"path\": \"system.runtime/4.3.0\",\n      \"hashPath\": \"system.runtime.4.3.0.nupkg.sha512\"\n    },\n    \"System.Runtime.CompilerServices.Unsafe/4.5.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-qUJMNWhbm9oZ3XaMFiEMiYmRPszbnXIkRIi7+4b2Md2xZ6JUOepf0/kY3S85qistRohl9OdMe4PsO+RdG2kTIQ==\",\n      \"path\": \"system.runtime.compilerservices.unsafe/4.5.1\",\n      \"hashPath\": \"system.runtime.compilerservices.unsafe.4.5.1.nupkg.sha512\"\n    },\n    \"System.Runtime.Extensions/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n      \"path\": \"system.runtime.extensions/4.3.0\",\n      \"hashPath\": \"system.runtime.extensions.4.3.0.nupkg.sha512\"\n    },\n    \"System.Runtime.Handles/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n      \"path\": \"system.runtime.handles/4.3.0\",\n      \"hashPath\": \"system.runtime.handles.4.3.0.nupkg.sha512\"\n    },\n    \"System.Runtime.InteropServices/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n      \"path\": \"system.runtime.interopservices/4.3.0\",\n      \"hashPath\": \"system.runtime.interopservices.4.3.0.nupkg.sha512\"\n    },\n    \"System.Runtime.InteropServices.RuntimeInformation/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n      \"path\": \"system.runtime.interopservices.runtimeinformation/4.3.0\",\n      \"hashPath\": \"system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512\"\n    },\n    \"System.Runtime.Numerics/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n      \"path\": \"system.runtime.numerics/4.3.0\",\n      \"hashPath\": \"system.runtime.numerics.4.3.0.nupkg.sha512\"\n    },\n    \"System.Security.AccessControl/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-aVjTe36YkO8FzfNhMLoPEzv3gF9rphoW9ngFhG/MH4zzEPLx07sNrgCLwMP4Wx2leI6qarMrGv21OwQXYUKLmw==\",\n      \"path\": \"system.security.accesscontrol/4.5.0\",\n      \"hashPath\": \"system.security.accesscontrol.4.5.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.Algorithms/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n      \"path\": \"system.security.cryptography.algorithms/4.3.0\",\n      \"hashPath\": \"system.security.cryptography.algorithms.4.3.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.Cng/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-O4tqXxWCD8y1IU1VTgzbuBFwoRahrADhDUxHjwezhHCsqyFNyQ5EytjWBxu0EsZuH14b4UO2pFkG063K2h/9Ug==\",\n      \"path\": \"system.security.cryptography.cng/4.5.0\",\n      \"hashPath\": \"system.security.cryptography.cng.4.5.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.Csp/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n      \"path\": \"system.security.cryptography.csp/4.3.0\",\n      \"hashPath\": \"system.security.cryptography.csp.4.3.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.Encoding/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n      \"path\": \"system.security.cryptography.encoding/4.3.0\",\n      \"hashPath\": \"system.security.cryptography.encoding.4.3.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.OpenSsl/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n      \"path\": \"system.security.cryptography.openssl/4.3.0\",\n      \"hashPath\": \"system.security.cryptography.openssl.4.3.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.Pkcs/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-1vv2x8cok3NAolee/nb6X/6PnTx+OBKUM3kt1Rlgg04uQ+IMwjc88xFIfJdwbYcvjlOtzT7CHba1pqVAu9tj/w==\",\n      \"path\": \"system.security.cryptography.pkcs/4.5.0\",\n      \"hashPath\": \"system.security.cryptography.pkcs.4.5.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.Primitives/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n      \"path\": \"system.security.cryptography.primitives/4.3.0\",\n      \"hashPath\": \"system.security.cryptography.primitives.4.3.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.X509Certificates/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n      \"path\": \"system.security.cryptography.x509certificates/4.3.0\",\n      \"hashPath\": \"system.security.cryptography.x509certificates.4.3.0.nupkg.sha512\"\n    },\n    \"System.Security.Cryptography.Xml/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-UvxfrEg7YG7U6BQO8WdQ4Nu1LFt2lqYQnoZefaK/2RDvjYdJ+norsVe4dwOqo14XiipgYY5xNUo6VhQXNbl2vg==\",\n      \"path\": \"system.security.cryptography.xml/4.5.0\",\n      \"hashPath\": \"system.security.cryptography.xml.4.5.0.nupkg.sha512\"\n    },\n    \"System.Security.Permissions/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-vDQ7q30Soe0a1cPhvxn+7IFmMeTG5IP+hTQrnKQDjTNpD2epqwbZSzMM2Git5TXBr4Kwwhc/0SEtJY0qPoiegA==\",\n      \"path\": \"system.security.permissions/4.5.0\",\n      \"hashPath\": \"system.security.permissions.4.5.0.nupkg.sha512\"\n    },\n    \"System.Security.Principal.Windows/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-WA9ETb/pY3BjnxKjBUHEgO59B7d/nnmjHFsqjJ2eDT780nD769CT1/bw2ia0Z6W7NqlcqokE6sKGKa6uw88XGA==\",\n      \"path\": \"system.security.principal.windows/4.5.0\",\n      \"hashPath\": \"system.security.principal.windows.4.5.0.nupkg.sha512\"\n    },\n    \"System.Text.Encoding/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n      \"path\": \"system.text.encoding/4.3.0\",\n      \"hashPath\": \"system.text.encoding.4.3.0.nupkg.sha512\"\n    },\n    \"System.Text.Encoding.CodePages/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-IRiEFUa5b/Gs5Egg8oqBVoywhtOeaO2KOx3j0RfcYY/raxqBuEK7NXRDgOwtYM8qbi+7S4RPXUbNt+ZxyY0/NQ==\",\n      \"path\": \"system.text.encoding.codepages/4.3.0\",\n      \"hashPath\": \"system.text.encoding.codepages.4.3.0.nupkg.sha512\"\n    },\n    \"System.Text.Encoding.Extensions/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n      \"path\": \"system.text.encoding.extensions/4.3.0\",\n      \"hashPath\": \"system.text.encoding.extensions.4.3.0.nupkg.sha512\"\n    },\n    \"System.Text.Encodings.Web/4.5.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-JF+wDdfFiRl3rz3dPMfR6aR568AW2J5CUMmhSflgHDz4zbVK4/00ax8UHnHyEMvblPewgNugjuA4oyoL8Pex2g==\",\n      \"path\": \"system.text.encodings.web/4.5.0\",\n      \"hashPath\": \"system.text.encodings.web.4.5.0.nupkg.sha512\"\n    },\n    \"System.Text.RegularExpressions/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n      \"path\": \"system.text.regularexpressions/4.3.0\",\n      \"hashPath\": \"system.text.regularexpressions.4.3.0.nupkg.sha512\"\n    },\n    \"System.Threading/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n      \"path\": \"system.threading/4.3.0\",\n      \"hashPath\": \"system.threading.4.3.0.nupkg.sha512\"\n    },\n    \"System.Threading.Tasks/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n      \"path\": \"system.threading.tasks/4.3.0\",\n      \"hashPath\": \"system.threading.tasks.4.3.0.nupkg.sha512\"\n    },\n    \"System.Threading.Tasks.Extensions/4.5.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-rckdhLJtzQ3EI+0BGuq7dUVtCSnerqAoAmL3S6oMRZ4VMZTL3Rq9DS8IDW57c6PYVebA4O0NbSA1BDvyE18UMA==\",\n      \"path\": \"system.threading.tasks.extensions/4.5.1\",\n      \"hashPath\": \"system.threading.tasks.extensions.4.5.1.nupkg.sha512\"\n    },\n    \"System.Threading.Tasks.Parallel/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-cbjBNZHf/vQCfcdhzx7knsiygoCKgxL8mZOeocXZn5gWhCdzHIq6bYNKWX0LAJCWYP7bds4yBK8p06YkP0oa0g==\",\n      \"path\": \"system.threading.tasks.parallel/4.3.0\",\n      \"hashPath\": \"system.threading.tasks.parallel.4.3.0.nupkg.sha512\"\n    },\n    \"System.Threading.Thread/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-OHmbT+Zz065NKII/ZHcH9XO1dEuLGI1L2k7uYss+9C1jLxTC9kTZZuzUOyXHayRk+dft9CiDf3I/QZ0t8JKyBQ==\",\n      \"path\": \"system.threading.thread/4.3.0\",\n      \"hashPath\": \"system.threading.thread.4.3.0.nupkg.sha512\"\n    },\n    \"System.Threading.Timer/4.0.1\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-saGfUV8uqVW6LeURiqxcGhZ24PzuRNaUBtbhVeuUAvky1naH395A/1nY0P2bWvrw/BreRtIB/EzTDkGBpqCwEw==\",\n      \"path\": \"system.threading.timer/4.0.1\",\n      \"hashPath\": \"system.threading.timer.4.0.1.nupkg.sha512\"\n    },\n    \"System.ValueTuple/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-cNLEvBX3d6MMQRZe3SMFNukVbitDAEpVZO17qa0/2FHxZ7Y7PpFRpr6m2615XYM/tYYYf0B+WyHNujqIw8Luwg==\",\n      \"path\": \"system.valuetuple/4.3.0\",\n      \"hashPath\": \"system.valuetuple.4.3.0.nupkg.sha512\"\n    },\n    \"System.Xml.ReaderWriter/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n      \"path\": \"system.xml.readerwriter/4.3.0\",\n      \"hashPath\": \"system.xml.readerwriter.4.3.0.nupkg.sha512\"\n    },\n    \"System.Xml.XDocument/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n      \"path\": \"system.xml.xdocument/4.3.0\",\n      \"hashPath\": \"system.xml.xdocument.4.3.0.nupkg.sha512\"\n    },\n    \"System.Xml.XmlDocument/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==\",\n      \"path\": \"system.xml.xmldocument/4.3.0\",\n      \"hashPath\": \"system.xml.xmldocument.4.3.0.nupkg.sha512\"\n    },\n    \"System.Xml.XPath/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-v1JQ5SETnQusqmS3RwStF7vwQ3L02imIzl++sewmt23VGygix04pEH+FCj1yWb+z4GDzKiljr1W7Wfvrx0YwgA==\",\n      \"path\": \"system.xml.xpath/4.3.0\",\n      \"hashPath\": \"system.xml.xpath.4.3.0.nupkg.sha512\"\n    },\n    \"System.Xml.XPath.XDocument/4.3.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-jw9oHHEIVW53mHY9PgrQa98Xo2IZ0ZjrpdOTmtvk+Rvg4tq7dydmxdNqUvJ5YwjDqhn75mBXWttWjiKhWP53LQ==\",\n      \"path\": \"system.xml.xpath.xdocument/4.3.0\",\n      \"hashPath\": \"system.xml.xpath.xdocument.4.3.0.nupkg.sha512\"\n    },\n    \"Telegram.Bot/14.6.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-ZEHe1AgqMGLv8WkxeKpnBe5wgkr4AscH7Jrix2hUFRfltCo3TkSh6g6P7/m2MacMsQ/kVd2Ah8/oOe+QrVBJhA==\",\n      \"path\": \"telegram.bot/14.6.0\",\n      \"hashPath\": \"telegram.bot.14.6.0.nupkg.sha512\"\n    },\n    \"IntelliTrader.Core/1.0.0\": {\n      \"type\": \"project\",\n      \"serviceable\": false,\n      \"sha512\": \"\"\n    },\n    \"Microsoft.NETCore.App/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-AvT774nTFgU8cYcGO9j1EMwuayKslxqYTurg32HGpWa2hEYNuW2+XgYVVNcZe6Ndbr84QX6fwaOZfd5n+1m2OA==\",\n      \"path\": \"microsoft.netcore.app/2.1.0\",\n      \"hashPath\": \"microsoft.netcore.app.2.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.NETCore.DotNetAppHost/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-f/47I60Wg3SrveTvnecCQhCZCAMYlUujWF15EQ/AZTqF/54qeEJjbCIAxKcZI8ToUYzSg6JdfrHggsgjCyCE9Q==\",\n      \"path\": \"microsoft.netcore.dotnetapphost/2.1.0\",\n      \"hashPath\": \"microsoft.netcore.dotnetapphost.2.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.NETCore.DotNetHostPolicy/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-p50yZYKzhH64lmArJgoKjtvsNehECa+/sAuOQzZh5uDNBTbRKxjN8IXP1e517xdVsgrFcSNxSEVDKZIOWVjGcQ==\",\n      \"path\": \"microsoft.netcore.dotnethostpolicy/2.1.0\",\n      \"hashPath\": \"microsoft.netcore.dotnethostpolicy.2.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.NETCore.DotNetHostResolver/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-fS9D8a+y55n6mHMbNqgHXaPGkjmpVH9h97OyrBxsCuo3Z8aQaFMJ5xIfmzji2ntUd/3truhMbSgSfIelHOkQpg==\",\n      \"path\": \"microsoft.netcore.dotnethostresolver/2.1.0\",\n      \"hashPath\": \"microsoft.netcore.dotnethostresolver.2.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.NETCore.Platforms/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-TT+QCi9LcxGTjBssH7S7n5+8DVcwfG4DYgXX7Dk7+BfZ4oVHj8Q0CbYk9glzAlHLsSt3bYzol+fOdra2iu6GOw==\",\n      \"path\": \"microsoft.netcore.platforms/2.1.0\",\n      \"hashPath\": \"microsoft.netcore.platforms.2.1.0.nupkg.sha512\"\n    },\n    \"Microsoft.NETCore.Targets/2.1.0\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-etaYwrLZQUS+b3UWTpCnUggd6SQ/ZIkZ5pHnoR7+dIWt/wp2Rv3CvMKOZISsrt7FYCHKwCxfcepuuyEWkQxADg==\",\n      \"path\": \"microsoft.netcore.targets/2.1.0\",\n      \"hashPath\": \"microsoft.netcore.targets.2.1.0.nupkg.sha512\"\n    },\n    \"NETStandard.Library/2.0.3\": {\n      \"type\": \"package\",\n      \"serviceable\": true,\n      \"sha512\": \"sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==\",\n      \"path\": \"netstandard.library/2.0.3\",\n      \"hashPath\": \"netstandard.library.2.0.3.nupkg.sha512\"\n    }\n  }\n}"
  },
  {
    "path": "IntelliTrader/IntelliTrader.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Remove=\"virtual-account.json\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Backtesting\\IntelliTrader.Backtesting.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Exchange.Base\\IntelliTrader.Exchange.Base.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Exchange.Binance\\IntelliTrader.Exchange.Binance.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Rules\\IntelliTrader.Rules.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Signals.Base\\IntelliTrader.Signals.Base.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Signals.TradingView\\IntelliTrader.Signals.TradingView.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Trading\\IntelliTrader.Trading.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Web\\IntelliTrader.Web.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Update=\"config\\core.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </None>\n    <None Update=\"config\\exchange.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </None>\n    <None Update=\"config\\logging.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </None>\n    <None Update=\"config\\paths.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </None>\n    <None Update=\"config\\signals.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </None>\n    <None Update=\"config\\trading.json\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </None>\n    <None Update=\"IntelliTrader.Web.deps.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"Start-IntelliTrader.bat\">\n      <CopyToOutputDirectory>Never</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n\n  <ItemGroup>\n    <Folder Include=\"data\\\" />\n    <Folder Include=\"log\\\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader/IntelliTrader.sh",
    "content": "#!/bin/bash\n\ndotnet bin/IntelliTrader.dll"
  },
  {
    "path": "IntelliTrader/Program.cs",
    "content": "﻿using Autofac;\nusing ExchangeSharp;\nusing IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            var parsedArgs = ParseCommandLineArgs(args);\n            if (parsedArgs.Count == 0)\n            {\n                PringWelcome();\n                StartCoreService();\n            }\n            else\n            {\n                if (parsedArgs.ContainsKey(\"encrypt\") && parsedArgs.ContainsKey(\"path\") &&\n                    parsedArgs.ContainsKey(\"publickey\") && parsedArgs.ContainsKey(\"privatekey\"))\n                {\n                    EncryptKeys(parsedArgs);\n                }\n                else\n                {\n                    PrintUsage();\n                }\n            }\n        }\n\n        private static void StartCoreService()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            coreService.Start();\n            Console.ReadLine();\n            coreService.Stop();\n        }\n\n        private static void PringWelcome()\n        {\n            var foregroundColorBackup = Console.ForegroundColor;\n            Console.ForegroundColor = ConsoleColor.Cyan;\n            Console.WriteLine();\n            Console.WriteLine(@\"  _____         _          _  _  _  _____                   _             \");\n            Console.WriteLine(@\"  \\_   \\ _ __  | |_   ___ | || |(_)/__   \\ _ __   __ _   __| |  ___  _ __ \");\n            Console.WriteLine(@\"   / /\\/| '_ \\ | __| / _ \\| || || |  / /\\/| '__| / _` | / _` | / _ \\| '__|\");\n            Console.WriteLine(@\"/\\/ /_  | | | || |_ |  __/| || || | / /   | |   | (_| || (_| ||  __/| |   \");\n            Console.WriteLine(@\"\\____/  |_| |_| \\__| \\___||_||_||_| \\/    |_|    \\__,_| \\__,_| \\___||_|   \");\n            Console.WriteLine();\n            Console.WriteLine(\"Welcome to IntelliTrader, The Intelligent Cryptocurrency Trading Bot.\");\n            Console.WriteLine(\"Always use Enter/Return key to exit the program to avoid corrupting the data.\");\n            Console.WriteLine();\n            Console.ForegroundColor = foregroundColorBackup;\n        }\n\n        private static void EncryptKeys(Dictionary<string, string> args)\n        {\n            var path = args[\"path\"];\n            var publicKey = args[\"publickey\"];\n            var privateKey = args[\"privatekey\"];\n\n            CryptoUtility.SaveUnprotectedStringsToFile(path, new string[] { publicKey, privateKey });\n            Console.WriteLine(\"All done! Press any key to exit...\");\n            Console.ReadKey();\n        }\n\n        private static void PrintUsage()\n        {\n            Console.WriteLine();\n            Console.WriteLine(\"Usage: dotnet IntelliTrader.dll --encrypt --path=<output_path> --publickey=<public_key> --privatekey=<private_key>\");\n            Console.WriteLine(\"The encrypted file is only valid for the current user and only on the computer it is created on.\");\n            Console.WriteLine();\n            Console.WriteLine(\"Press any key to exit...\");\n            Console.ReadKey();\n        }\n\n        private static Dictionary<string, string> ParseCommandLineArgs(string[] args)\n        {\n            var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);\n            foreach (string a in args)\n            {\n                int idx = a.IndexOf('=');\n                string key = (idx < 0 ? a.TrimStart('-') : a.Substring(0, idx)).ToLowerInvariant().TrimStart('-');\n                string value = (idx < 0 ? string.Empty : a.Substring(idx + 1));\n                dict[key] = value;\n            }\n            return dict;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader/Properties/PublishProfiles/FolderProfile.pubxml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nThis file is used by the publish/package process of your project. You can customize the behavior of this process\nby editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121. \n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <PublishProtocol>FileSystem</PublishProtocol>\n    <Configuration>Release</Configuration>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n    <PublishDir>..\\Publish\\bin</PublishDir>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "IntelliTrader/config/backtesting.json",
    "content": "﻿{\n  \"Backtesting\": {\n    \"Enabled\": false,\n    \"Replay\": false,\n    \"ReplayOutput\": true,\n    \"ReplaySpeed\": 50,\n    \"ReplayStartIndex\": null,\n    \"ReplayEndIndex\": null,\n    \"DeleteLogs\": true,\n    \"DeleteAccountData\": true,\n    \"CopyAccountDataPath\": null,\n    \"TradingSpeedEasing\": 0,\n    \"TradingRulesSpeedEasing\": 0,\n    \"SignalRulesSpeedEasing\": 0,\n    \"SnapshotsInterval\": 1,\n    \"SnapshotsPath\": \"data/backtesting\"\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/core.json",
    "content": "{\n  \"Core\": {\n    \"DebugMode\": false,\n    \"PasswordProtected\": true,\n    \"Password\": \"b84967c4f073b71405404f3719c788cd\",\n    \"InstanceName\": \"Main\",\n    \"TimezoneOffset\": 1,\n    \"HealthCheckEnabled\": true,\n    \"HealthCheckInterval\": 180,\n    \"HealthCheckSuspendTradingTimeout\": 900,\n    \"HealthCheckFailuresToRestartServices\": 5\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/exchange.json",
    "content": "{\n  \"Exchange\": {\n    \"KeysPath\": \"data/keys.bin\",\n    \"RateLimitOccurences\": 40,\n    \"RateLimitTimeframe\": 10\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/logging.json",
    "content": "{\n  \"Logging\": {\n    \"Enabled\": true,\n    \"MinimumLevel\": {\n      \"Default\": \"Verbose\",\n      \"Override\": {\n        \"System\": \"Warning\",\n        \"Microsoft\": \"Warning\"\n      }\n    },\n    \"WriteTo\": [\n      {\n        \"Name\": \"Logger\",\n        \"Args\": {\n          \"configureLogger\": {\n            \"WriteTo\": [\n              {\n                \"Name\": \"Console\",\n                \"Args\": {\n                  \"outputTemplate\": \"{Timestamp:HH:mm:ss} [{Level:u3}] {Message}{NewLine}{Exception}\",\n                  \"theme\": \"Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Literate, Serilog.Sinks.Console\",\n                  \"restrictedToMinimumLevel\": \"Information\"\n                }\n              },\n              {\n                \"Name\": \"RollingFile\",\n                \"Args\": {\n                  \"outputTemplate\": \"[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] {Message}{NewLine}{Exception}\",\n                  \"pathFormat\": \"log/{Date}-general.txt\",\n                  \"retainedFileCountLimit\": 1000\n                }\n              }\n            ],\n            \"Filter\": [\n              {\n                \"Name\": \"ByIncludingOnly\",\n                \"Args\": {\n                  \"expression\": \"Trade is null\"\n                }\n              }\n            ]\n          }\n        }\n      },\n      {\n        \"Name\": \"Logger\",\n        \"Args\": {\n          \"configureLogger\": {\n            \"WriteTo\": [\n              {\n                \"Name\": \"RollingFile\",\n                \"Args\": {\n                  \"outputTemplate\": \"[{Timestamp:HH:mm:ss.fff}] {Message}{NewLine}\",\n                  \"pathFormat\": \"log/{Date}-trades.txt\",\n                  \"retainedFileCountLimit\": 1000\n                }\n              }\n            ],\n            \"Filter\": [\n              {\n                \"Name\": \"ByIncludingOnly\",\n                \"Args\": {\n                  \"expression\": \"Trade is not null\"\n                }\n              }\n            ]\n          }\n        }\n      }\n    ]\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/notification.json",
    "content": "{\n  \"Notification\": {\n    \"Enabled\": false,\n    \"TelegramEnabled\": true,\n    \"TelegramBotToken\": \"\",\n    \"TelegramChatId\": 0,\n    \"TelegramAlertsEnabled\": true\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/paths.json",
    "content": "﻿{\n  \"Paths\": {\n    \"Core\": \"core.json\",\n    \"Logging\": \"logging.json\",\n    \"Trading\": \"trading.json\",\n    \"Exchange\": \"exchange.json\",\n    \"Signals\": \"signals.json\",\n    \"Rules\": \"rules.json\",\n    \"Notification\": \"notification.json\",\n    \"Web\": \"web.json\",\n    \"Backtesting\": \"backtesting.json\"\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/rules.json",
    "content": "{\n  \"Rules\": {\n    \"Modules\": [\n      {\n        \"Module\": \"Signals\",\n        \"Configuration\": {\n          \"ProcessingMode\": \"AllMatches\",\n          \"CheckInterval\": 0.1\n        },\n        \"Entries\": [\n          {\n            \"Enabled\": false,\n            \"Name\": \"Buy-Arbitrage\",\n            \"Action\": \"Arbitrage\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"MinArbitrage\": 4\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Safe\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinVolatility\": 2.5,\n                \"MaxVolatility\": 10,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.25,\n                \"MinVolume\": 100000,\n                \"MaxPriceChange\": 6\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinRating\": 0.1,\n                \"MinPriceChange\": 1.5,\n                \"MaxPriceChange\": 12\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 5,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": -0.35,\n                \"MaxGlobalRating\": 1.0,\n                \"MaxSpread\": 0.25,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Bull\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinVolatility\": 3,\n                \"MaxVolatility\": 12,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.25,\n                \"MinVolume\": 100000,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinRating\": 0.1,\n                \"MinPriceChange\": 2,\n                \"MaxPriceChange\": 12\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 4,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": 0.25,\n                \"MaxSpread\": 0.35,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": false,\n            \"Name\": \"Buy-TUSDT\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-5m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"MinGlobalRating\": -0.28,\n                \"MaxGlobalRating\": -0.15,\n                \"MaxSpread\": 0.35,\n                \"Pairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Volume-Spike\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MinRating\": 0.4,\n                \"MinPriceChange\": 1,\n                \"MaxPriceChange\": 8,\n                \"MinVolumeChange\": 500,\n                \"MaxVolatility\": 12\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.4,\n                \"MinPriceChange\": 1.5,\n                \"MaxPriceChange\": 9,\n                \"MinVolumeChange\": 200\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.25,\n                \"MinVolume\": 200000,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinPriceChange\": 1,\n                \"MaxPriceChange\": 10\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 2,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": -0.10,\n                \"MaxGlobalRating\": 1.0,\n                \"MaxSpread\": 1,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Pump\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinVolumeChange\": 20,\n                \"MaxVolumeChange\": 200,\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-5m\",\n                \"MinRating\": 0.30,\n                \"MinPriceChange\": 3,\n                \"MinVolumeChange\": 0,\n                \"MaxPriceChange\": 10\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.0,\n                \"MinVolume\": 100000,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinPriceChange\": 3,\n                \"MaxPriceChange\": 10\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 4,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": -0.30,\n                \"MaxGlobalRating\": 1.0,\n                \"MaxSpread\": 0.6\n              }\n            ],\n            \"Trailing\": {\n              \"Enabled\": true,\n              \"MinDuration\": 10,\n              \"MaxDuration\": 60,\n              \"StartConditions\": [\n                {\n                  \"Signal\": \"TV-5m\",\n                  \"MinVolumeChange\": 200\n                }\n              ]\n            }\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Safe\",\n            \"Action\": \"Swap\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.20,\n                \"MaxRating\": 0.35,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.10,\n                \"MaxRating\": 0.25,\n                \"MinVolume\": 400000,\n                \"MinRatingChange\": 0,\n                \"MaxPriceChange\": 6\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinRating\": 0.10,\n                \"MaxRating\": 0.25,\n                \"MinPriceChange\": 1,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinRatingChange\": 0,\n                \"MinPriceChange\": 2,\n                \"MaxPriceChange\": 12\n              },\n              {\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1,\n                \"MaxSpread\": 0.35,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"Module\": \"Trading\",\n        \"Configuration\": {\n          \"ProcessingMode\": \"AllMatches\",\n          \"CheckInterval\": 0.1\n        },\n        \"Entries\": [\n          {\n            \"Enabled\": true,\n            \"Name\": \"TUSD-DCA\",\n            \"Modifiers\": {\n              \"BuyEnabled\": true,\n              \"BuyDCAEnabled\": true,\n              \"SellMargin\": 0.20,\n              \"SellTrailing\": 0.15,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.40,\n                  \"BuySamePairTimeout\": 0,\n                  \"BuyTrailing\": -0.20,\n                  \"BuyTrailingStopMargin\": 1.00,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.15\n                },\n                {\n                  \"Margin\": -1.25,\n                  \"BuySamePairTimeout\": 180,\n                  \"BuyTrailing\": -0.20,\n                  \"BuyTrailingStopMargin\": 1.50,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15\n                },\n                {\n                  \"Margin\": -2.50,\n                  \"BuySamePairTimeout\": 300,\n                  \"BuyTrailing\": -0.25,\n                  \"BuyTrailingStopMargin\": 2.00,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.10\n                },\n                {\n                  \"Margin\": -5.50,\n                  \"BuySamePairTimeout\": 1800,\n                  \"BuyTrailing\": -0.25,\n                  \"BuyTrailingStopMargin\": 2.00,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.05,\n                  \"SellTrailing\": 0.10\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -1.00,\n                \"MaxGlobalRating\": -0.10,\n                \"Pairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Apocalypse\",\n            \"Modifiers\": {\n              \"BuyEnabled\": false,\n              \"BuyDCAEnabled\": false,\n              \"SellMargin\": -0.50,\n              \"SellTrailing\": 0,\n              \"SellDCAMargin\": -0.50,\n              \"SellDCATrailing\": 0\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -1,\n                \"MaxGlobalRating\": -0.40\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Bear\",\n            \"Modifiers\": {\n              \"BuyDCAEnabled\": false,\n              \"BuyTrailing\": -0.45,\n              \"SellMargin\": 0.40,\n              \"SellTrailing\": 0.25,\n              \"SellTrailingStopMargin\": 0.30,\n              \"MaxPairs\": 4,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -4,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.35,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -8,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.10,\n                  \"BuyTrailing\": -0.55,\n                  \"BuySamePairTimeout\": 600\n                },\n                {\n                  \"Margin\": -15,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.10,\n                  \"BuyTrailing\": -1,\n                  \"BuySamePairTimeout\": 1200\n                },\n                {\n                  \"Margin\": -20,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": -1.5,\n                  \"BuySamePairTimeout\": 2880\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -0.40,\n                \"MaxGlobalRating\": -0.15,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"BuyDCAEnabled\": false,\n            \"Name\": \"Boring\",\n            \"Modifiers\": {\n              \"BuyTrailing\": -0.35,\n              \"SellMargin\": 0.50,\n              \"SellTrailing\": 0.35,\n              \"SellTrailingStopMargin\": 0.40,\n              \"MaxPairs\": 5,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -2.5,\n                  \"SellMargin\": 0.30,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.30,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -5,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.45,\n                  \"BuySamePairTimeout\": 300\n                },\n                {\n                  \"Margin\": -8,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.10,\n                  \"SellTrailingStopMargin\": 0.10,\n                  \"BuyTrailing\": -0.75,\n                  \"BuySamePairTimeout\": 900\n                },\n                {\n                  \"Margin\": -12,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": -1.5,\n                  \"BuySamePairTimeout\": 1800\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -0.15,\n                \"MaxGlobalRating\": 0.15,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Bull\",\n            \"Modifiers\": {\n              \"BuyTrailing\": -0.25,\n              \"SellMargin\": 0.60,\n              \"SellTrailing\": 0.45,\n              \"SellTrailingStopMargin\": 0.50,\n              \"MaxPairs\": 6,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.35,\n                  \"SellMargin\": 0.40,\n                  \"SellTrailing\": 0.35,\n                  \"SellTrailingStopMargin\": 0.35,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -2,\n                  \"SellMargin\": 0.35,\n                  \"SellTrailing\": 0.30,\n                  \"SellTrailingStopMargin\": 0.30,\n                  \"BuyTrailing\": -0.35,\n                  \"BuySamePairTimeout\": 180\n                },\n                {\n                  \"Margin\": -7,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15,\n                  \"SellTrailingStopMargin\": 0.15,\n                  \"BuyTrailing\": -0.55,\n                  \"BuySamePairTimeout\": 600\n                },\n                {\n                  \"Margin\": -12,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": -1.5,\n                  \"BuySamePairTimeout\": 1400\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": 0.15,\n                \"MaxGlobalRating\": 1.00\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"DCA\",\n            \"Modifiers\": {\n              \"BuyDCAEnabled\": false\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0.30\n              },\n              {\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"SuperBull-HP-DCA\",\n            \"Modifiers\": {\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.25,\n                  \"SellMargin\": 0.25,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.25,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -0.75,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.45,\n                  \"BuySamePairTimeout\": 300\n                },\n                {\n                  \"Margin\": -1.50,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15,\n                  \"SellTrailingStopMargin\": 0.15,\n                  \"BuyTrailing\": -0.65,\n                  \"BuySamePairTimeout\": 600\n                },\n                {\n                  \"Margin\": -4.0,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": 2.0,\n                  \"BuySamePairTimeout\": 1200\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"MinGlobalRating\": 0.30\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Bull-HP-DCA\",\n            \"Modifiers\": {\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.35,\n                  \"SellMargin\": 0.25,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.25,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -1.5,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.35,\n                  \"BuySamePairTimeout\": 180\n                },\n                {\n                  \"Margin\": -5.5,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15,\n                  \"SellTrailingStopMargin\": 0.15,\n                  \"BuyTrailing\": -0.55,\n                  \"BuySamePairTimeout\": 300\n                },\n                {\n                  \"Margin\": -10,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": 2.0,\n                  \"BuySamePairTimeout\": 1200\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0.30\n              },\n              {\n                \"MinGlobalRating\": 0.20,\n                \"MaxGlobalRating\": 0.30\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Only-Bull\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\" ],\n              \"SwapTimeout\": 1800\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 2,\n                \"MinGlobalRating\": 0.30,\n                \"MaxGlobalRating\": 1.00\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Money-Pump\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Pump\" ],\n              \"SwapTimeout\": 300\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 0,\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Small-Bag\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Volume-Spike\", \"Buy-Safe\" ],\n              \"SwapTimeout\": 900\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": -0.30\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": -0.30\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 1,\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Medium-Bag\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\" ],\n              \"SwapTimeout\": 3600\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": -0.2\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": -0.2\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 2,\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Big-Bag\",\n            \"Modifiers\": {\n              \"BuyDCAEnabled\": false,\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Swap-Safe\" ],\n              \"SwapTimeout\": 7200\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": -0.2\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MinDCALevel\": 3\n              },\n              {\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Run-Forest-Run\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\", \"Swap-Safe\" ],\n              \"SwapTimeout\": 300\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"MinMarginChange\": 3,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-TUSDT\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\", \"Buy-Volume-Spike\" ],\n              \"SwapTimeout\": 300\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -0.10,\n                \"MaxGlobalRating\": 1.00,\n                \"Pairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Exclude-BNB\",\n            \"Modifiers\": {\n              \"BuyEnabled\": false,\n              \"BuyDCAEnabled\": false,\n              \"SellEnabled\": false\n            },\n            \"Conditions\": [\n              {\n                \"Pairs\": [\n                  \"BNBBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"BNB-Top-Up\",\n            \"Modifiers\": {\n              \"BuyEnabled\": true,\n              \"BuyDCAEnabled\": true,\n              \"BuyDCASamePairTimeout\": 0,\n              \"BuyMaxCost\": 0.003,\n              \"RepeatLastDCALevel\": true,\n              \"DCALevels\": [\n                {\n                  \"Margin\": 100\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"Pairs\": [ \"BNBBTC\" ],\n                \"MaxAmount\": 1\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Exclude-Pairs\",\n            \"Modifiers\": {\n              \"BuyEnabled\": false\n            },\n            \"Conditions\": [\n              {\n                \"Pairs\": [\n                  \"ADABTC\",\n                  \"BCHBTC\",\n                  \"ETHBTC\",\n                  \"XRPBTC\",\n                  \"LTCBTC\",\n                  \"EOSBTC\",\n                  \"XMRBTC\",\n                  \"ZECBTC\",\n                  \"ICXBTC\",\n                  \"DASHBTC\",\n                  \"XLMBTC\",\n                  \"OAXBTC\",\n                  \"RCNBTC\",\n                  \"QSPBTC\",\n                  \"SUBBTC\",\n                  \"BNTBTC\",\n                  \"AEBTC\",\n                  \"TNBBTC\",\n                  \"XEMBTC\",\n                  \"TNTBTC\",\n                  \"TRXBTC\",\n                  \"IOTABTC\",\n                  \"AMBBTC\",\n                  \"DLTBTC\",\n                  \"CDTBTC\",\n                  \"CNDBTC\",\n                  \"CHATBTC\",\n                  \"REQBTC\",\n                  \"CVCBTC\",\n                  \"DNTBTC\",\n                  \"IOTXBTC\",\n                  \"RPXBTC\",\n                  \"VIBBTC\"\n                ],\n                \"MaxGlobalRating\": 0.3\n              }\n            ]\n          },\n          {\n            \"Enabled\": false,\n            \"Name\": \"Arbitrage\",\n            \"Modifiers\": {\n              \"BuyEnabled\": true,\n              \"ArbitrageEnabled\": true,\n              \"ArbitrageMarkets\": [ \"ETH\", \"BNB\" ],\n              \"ArbitrageBuyMultiplier\": 0.985,\n              \"ArbitrageSellMultiplier\": 0.985,\n              \"ArbitrageSignalRules\": [ \"Buy-Arbitrage\" ]\n            },\n            \"Conditions\": [\n              {\n                \"MinArbitrage\": 3\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/signals.json",
    "content": "{\n  \"Signals\": {\n    \"Enabled\": true,\n    \"GlobalRatingSignals\": [\n      \"TV-5m\",\n      \"TV-15m\",\n      \"TV-1h\"\n    ],\n    \"Definitions\": [\n      {\n        \"Name\": \"TV-1m\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 1,\n          \"SignalPeriod\": 1,\n          \"VolatilityPeriod\": \"Day\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-5m\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 3,\n          \"SignalPeriod\": 5,\n          \"VolatilityPeriod\": \"Day\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-15m\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 5,\n          \"SignalPeriod\": 15,\n          \"VolatilityPeriod\": \"Week\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-1h\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 7,\n          \"SignalPeriod\": 60,\n          \"VolatilityPeriod\": \"Month\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-4h\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 15,\n          \"SignalPeriod\": 240,\n          \"VolatilityPeriod\": \"Month\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-1d\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 60,\n          \"SignalPeriod\": 1440,\n          \"VolatilityPeriod\": \"Month\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      }\n    ]\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/trading.json",
    "content": "{\n  \"Trading\": {\n    \"Enabled\": true,\n    \"Market\": \"BTC\",\n    \"Exchange\": \"Binance\",\n    \"MaxPairs\": 5,\n    \"MinCost\": 0.000999,\n    \"TradePriceType\": \"Bid\",\n    \"ExcludedPairs\": [],\n    \"BuyEnabled\": true,\n    \"BuyType\": \"Market\",\n    \"BuyMaxCost\": 0.03,\n    \"BuyMultiplier\": 1,\n    \"BuyMinBalance\": 0,\n    \"BuySamePairTimeout\": 15,\n    \"BuyTrailing\": -0.25,\n    \"BuyTrailingStopMargin\": 1.35,\n    \"BuyTrailingStopAction\": \"Buy\",\n    \"BuyDCAEnabled\": true,\n    \"BuyDCAMultiplier\": 1,\n    \"BuyDCAMinBalance\": 0,\n    \"BuyDCASamePairTimeout\": 180,\n    \"BuyDCATrailing\": -0.25,\n    \"BuyDCATrailingStopMargin\": 2,\n    \"BuyDCATrailingStopAction\": \"Buy\",\n    \"SellEnabled\": true,\n    \"SellType\": \"Market\",\n    \"SellMargin\": 1.0,\n    \"SellTrailing\": 0.65,\n    \"SellTrailingStopMargin\": 0.7,\n    \"SellTrailingStopAction\": \"Sell\",\n    \"SellStopLossEnabled\": false,\n    \"SellStopLossAfterDCA\": false,\n    \"SellStopLossMinAge\": 0,\n    \"SellStopLossMargin\": -3,\n    \"SellDCAMargin\": 0.5,\n    \"SellDCATrailing\": 0.35,\n    \"SellDCATrailingStopMargin\": 0.5,\n    \"SellDCATrailingStopAction\": \"Sell\",\n    \"RepeatLastDCALevel\": false,\n    \"DCALevels\": [],\n    \"TradingCheckInterval\": 0.1,\n    \"AccountRefreshInterval\": 360,\n    \"AccountInitialBalance\": 1,\n    \"AccountInitialBalanceDate\": \"2018-04-08T00:00:00+00:00\",\n    \"AccountFilePath\": \"data/exchange-account.json\",\n    \"VirtualTrading\": true,\n    \"VirtualTradingFees\": 0.0005,\n    \"VirtualAccountInitialBalance\": 1,\n    \"VirtualAccountFilePath\": \"data/virtual-account.json\"\n  }\n}"
  },
  {
    "path": "IntelliTrader/config/web.json",
    "content": "{\n  \"Web\": {\n    \"Enabled\": true,\n    \"DebugMode\": false,\n    \"ReadOnlyMode\": false,\n    \"Port\": 7000,\n    \"SSLEnabled\": false,\n    \"SSLCertPath\": \"data/cert.pfx\",\n    \"SSLCertPassword\": \"certpass\"\n  }\n}"
  },
  {
    "path": "IntelliTrader/data/encrypt-keys.bat",
    "content": "dotnet ..\\bin\\Debug\\netcoreapp2.1\\IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key\npause"
  },
  {
    "path": "IntelliTrader/data/encrypt-keys.sh",
    "content": "#!/bin/bash\n\ndotnet bin/IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key"
  },
  {
    "path": "IntelliTrader.Backtesting/AppModule.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\nusing System;\n\nnamespace IntelliTrader.Backtesting\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<BacktestingService>().As<IBacktestingService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.BacktestingService).SingleInstance();\n\n            var backtestingConfig = Application.ConfigProvider.GetSection<BacktestingConfig>(Constants.ServiceNames.BacktestingService);\n            if (backtestingConfig.Enabled && backtestingConfig.Replay)\n            {\n                builder.RegisterType<BacktestingExchangeService>().Named<IExchangeService>(Constants.ServiceNames.BacktestingExchangeService).As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.BacktestingExchangeService).SingleInstance();\n                builder.RegisterType<BacktestingSignalsService>().As<ISignalsService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.BacktestingSignalsService).SingleInstance();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/Config/BacktestingConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Backtesting\n{\n    internal class BacktestingConfig : IBacktestingConfig\n    {\n        public bool Enabled { get; set; }\n        public bool Replay { get; set; }\n        public bool ReplayOutput { get; set; }\n        public double ReplaySpeed { get; set; }\n        public int? ReplayStartIndex { get; set; }\n        public int? ReplayEndIndex { get; set; }\n        public bool DeleteLogs { get; set; }\n        public bool DeleteAccountData { get; set; }\n        public string CopyAccountDataPath { get; set; }\n        public int TradingSpeedEasing { get; set; }\n        public int TradingRulesSpeedEasing { get; set; }\n        public int SignalRulesSpeedEasing { get; set; }\n        public int SnapshotsInterval { get; set; }\n        public string SnapshotsPath { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/IntelliTrader.Backtesting.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"ZeroFormatter\" Version=\"1.6.4\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Exchange.Base\\IntelliTrader.Exchange.Base.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Signals.Base\\IntelliTrader.Signals.Base.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Trading\\IntelliTrader.Trading.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Backtesting/Model/SignalData.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Signals.Base;\nusing ZeroFormatter;\n\nnamespace IntelliTrader.Backtesting\n{\n    [ZeroFormattable]\n    public class SignalData : ISignal\n    {\n        [Index(0)]\n        public virtual string Name { get; set; }\n        [Index(1)]\n        public virtual string Pair { get; set; }\n        [Index(2)]\n        public virtual long? Volume { get; set; }\n        [Index(3)]\n        public virtual double? VolumeChange { get; set; }\n        [Index(4)]\n        public virtual decimal? Price { get; set; }\n        [Index(5)]\n        public virtual decimal? PriceChange { get; set; }\n        [Index(6)]\n        public virtual double? Rating { get; set; }\n        [Index(7)]\n        public virtual double? RatingChange { get; set; }\n        [Index(8)]\n        public virtual double? Volatility { get; set; }\n\n        public ISignal ToSignal()\n        {\n            return new Signal\n            {\n                Name = Name,\n                Pair = Pair,\n                Volume = Volume,\n                VolumeChange = VolumeChange,\n                Price = Price,\n                PriceChange = PriceChange,\n                Rating = Rating,\n                RatingChange = RatingChange,\n                Volatility = Volatility\n            };\n        }\n\n        public static SignalData FromSignal(ISignal signal)\n        {\n            return new SignalData\n            {\n                Name = signal.Name,\n                Pair = signal.Pair,\n                Volume = signal.Volume,\n                VolumeChange = signal.VolumeChange,\n                Price = signal.Price,\n                PriceChange = signal.PriceChange,\n                Rating = signal.Rating,\n                RatingChange = signal.RatingChange,\n                Volatility = signal.Volatility\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/Model/TickerData.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Exchange.Base;\nusing ZeroFormatter;\n\nnamespace IntelliTrader.Backtesting\n{\n    [ZeroFormattable]\n    public class TickerData : ITicker\n    {\n        [Index(0)]\n        public virtual string Pair { get; set; }\n        [Index(1)]\n        public virtual decimal BidPrice { get; set; }\n        [Index(2)]\n        public virtual decimal AskPrice { get; set; }\n        [Index(3)]\n        public virtual decimal LastPrice { get; set; }\n\n        public ITicker ToTicker()\n        {\n            return new Ticker\n            {\n                Pair = Pair,\n                BidPrice = BidPrice,\n                AskPrice = AskPrice,\n                LastPrice = LastPrice\n            };\n        }\n\n        public static TickerData FromTicker(ITicker ticker)\n        {\n            return new TickerData\n            {\n                Pair = ticker.Pair,\n                BidPrice = ticker.BidPrice,\n                AskPrice = ticker.AskPrice,\n                LastPrice = ticker.LastPrice\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/Services/BacktestingExchangeService.cs",
    "content": "﻿using ExchangeSharp;\nusing IntelliTrader.Core;\nusing IntelliTrader.Exchange.Base;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace IntelliTrader.Backtesting\n{\n    public class BacktestingExchangeService : ExchangeService\n    {\n        private readonly IBacktestingService backtestingService;\n        private ConcurrentBag<string> markets;\n\n        public BacktestingExchangeService(ILoggingService loggingService, IHealthCheckService healthCheckService, \n            ITasksService tasksService, IBacktestingService backtestingService)\n            : base(loggingService, healthCheckService, tasksService)\n        {\n            this.backtestingService = backtestingService;\n        }\n\n        public override void Start(bool virtualTrading)\n        {\n            loggingService.Info(\"Start Backtesting Exchange service...\");\n\n            Api = InitializeApi();\n\n            loggingService.Info(\"Backtesting Exchange service started\");\n        }\n\n        public override void Stop()\n        {\n            loggingService.Info(\"Stop Backtesting Exchange service...\");\n\n\n            loggingService.Info(\"Backtesting Exchange service stopped\");\n        }\n\n        protected override ExchangeAPI InitializeApi()\n        {\n            return new ExchangeBinanceAPI();\n        }\n\n        public override IEnumerable<string> GetMarkets()\n        {\n            if (markets == null && backtestingService.GetCurrentTickers() != null)\n            {\n                this.markets = new ConcurrentBag<string>(backtestingService.GetCurrentTickers().Keys\n                    .Select(pair => GetPairMarket(pair)).Distinct().ToList());\n            }\n            return markets.AsEnumerable() ?? new List<string>();\n        }\n\n        public override IEnumerable<string> GetMarketPairs(string market)\n        {\n            return backtestingService.GetCurrentTickers().Keys.Where(t => t.EndsWith(market));\n        }\n\n        public override decimal GetPrice(string pair, TradePriceType priceType)\n        {\n            if (backtestingService.GetCurrentTickers().TryGetValue(pair, out ITicker ticker))\n            {\n                if (priceType == TradePriceType.Ask)\n                {\n                    return ticker.AskPrice;\n                }\n                else if (priceType == TradePriceType.Bid)\n                {\n                    return ticker.BidPrice;\n                }\n                else\n                {\n                    return ticker.LastPrice;\n                }\n            }\n            else\n            {\n                return 0;\n            }\n        }\n\n        public override decimal GetPriceSpread(string pair)\n        {\n            if (backtestingService.GetCurrentTickers().TryGetValue(pair, out ITicker ticker))\n            {\n                return Utils.CalculatePercentage(ticker.BidPrice, ticker.AskPrice);\n            }\n            else\n            {\n                return 0;\n            }\n        }\n\n        public override Arbitrage GetArbitrage(string pair, string tradingMarket, List<ArbitrageMarket> arbitrageMarkets = null, ArbitrageType? arbitrageType = null)\n        {\n            if (arbitrageMarkets == null || !arbitrageMarkets.Any())\n            {\n                arbitrageMarkets = new List<ArbitrageMarket> { ArbitrageMarket.ETH, ArbitrageMarket.BNB, ArbitrageMarket.USDT };\n            }\n            Arbitrage arbitrage = new Arbitrage\n            {\n                Market = arbitrageMarkets.First(),\n                Type = arbitrageType ?? ArbitrageType.Direct\n            };\n\n            try\n            {\n                if (tradingMarket == Constants.Markets.BTC)\n                {\n                    foreach (var market in arbitrageMarkets)\n                    {\n                        string marketPair = ChangeMarket(pair, market.ToString());\n                        string arbitragePair = GetArbitrageMarketPair(market);\n\n                        if (marketPair != pair && \n                            backtestingService.GetCurrentTickers().TryGetValue(pair, out ITicker pairTicker) &&\n                            backtestingService.GetCurrentTickers().TryGetValue(marketPair, out ITicker marketTicker) &&\n                            backtestingService.GetCurrentTickers().TryGetValue(arbitragePair, out ITicker arbitrageTicker))\n                        {\n                            decimal directArbitragePercentage = 0;\n                            decimal reverseArbitragePercentage = 0;\n\n                            if (market == ArbitrageMarket.ETH)\n                            {\n                                directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100;\n                                reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100;\n                            }\n                            else if (market == ArbitrageMarket.BNB)\n                            {\n                                directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100;\n                                reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100;\n                            }\n                            else if (market == ArbitrageMarket.USDT)\n                            {\n                                directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice / arbitrageTicker.AskPrice - 1) * 100;\n                                reverseArbitragePercentage = (arbitrageTicker.BidPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100;\n                            }\n\n                            if ((directArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Direct))\n                            {\n                                arbitrage.IsAssigned = true;\n                                arbitrage.Market = market;\n                                arbitrage.Type = ArbitrageType.Direct;\n                                arbitrage.Percentage = directArbitragePercentage;\n                            }\n\n                            if ((reverseArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Reverse))\n                            {\n                                arbitrage.IsAssigned = true;\n                                arbitrage.Market = market;\n                                arbitrage.Type = ArbitrageType.Reverse;\n                                arbitrage.Percentage = reverseArbitragePercentage;\n                            }\n                        }\n                    }\n                }\n            }\n            catch { }\n            return arbitrage;\n        }\n\n        public override string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket)\n        {\n            if (arbitrageMarket == ArbitrageMarket.ETH)\n            {\n                return Constants.Markets.ETH + Constants.Markets.BTC;\n            }\n            else if (arbitrageMarket == ArbitrageMarket.BNB)\n            {\n                return Constants.Markets.BNB + Constants.Markets.BTC;\n            }\n            else if (arbitrageMarket == ArbitrageMarket.USDT)\n            {\n                return Constants.Markets.BTC + Constants.Markets.USDT;\n            }\n            else\n            {\n                throw new NotSupportedException($\"Unsupported arbitrage market: {arbitrageMarket}\");\n            }\n        }\n\n\n\n\n        #region Not Needed For Backtesting\n\n        public override IOrderDetails PlaceOrder(IOrder order)\n        {\n            throw new NotImplementedException();\n        }\n\n        public override IEnumerable<ITicker> GetTickers()\n        {\n            throw new NotImplementedException();\n        }\n\n        public override Dictionary<string, decimal> GetAvailableAmounts()\n        {\n            throw new NotImplementedException();\n        }\n\n        public override IEnumerable<IOrderDetails> GetTrades(string pair)\n        {\n            throw new NotImplementedException();\n        }\n\n        #endregion Not Needed For Backtesting\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/Services/BacktestingService.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Threading;\nusing IntelliTrader.Signals.Base;\nusing IntelliTrader.Trading;\n\nnamespace IntelliTrader.Backtesting\n{\n    internal class BacktestingService : ConfigrableServiceBase<BacktestingConfig>, IBacktestingService\n    {\n        public const string SNAPSHOT_FILE_EXTENSION = \"bin\";\n\n        public override string ServiceName => Constants.ServiceNames.BacktestingService;\n\n        IBacktestingConfig IBacktestingService.Config => Config;\n\n        public object SyncRoot { get; private set; } = new object();\n\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITasksService tasksService;\n        private ISignalsService signalsService;\n        private ITradingService tradingService;\n        private BacktestingLoadSnapshotsTimedTask backtestingLoadSnapshotsTimedTask;\n        private BacktestingSaveSnapshotsTimedTask backtestingSaveSnapshotsTimedTask;\n\n        public BacktestingService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tasksService = tasksService;\n        }\n\n        public void Start()\n        {\n            loggingService.Info($\"Start Backtesting service... (Replay: {Config.Replay})\");\n\n            signalsService = Application.Resolve<ISignalsService>();\n            tradingService = Application.Resolve<ITradingService>();\n\n            if (Config.Replay)\n            {\n                backtestingLoadSnapshotsTimedTask = tasksService.AddTask(\n                    name: nameof(BacktestingLoadSnapshotsTimedTask),\n                    task: new BacktestingLoadSnapshotsTimedTask(loggingService, healthCheckService, tradingService, this),\n                    interval: Config.SnapshotsInterval / Config.ReplaySpeed * 1000,\n                    startDelay: Constants.TaskDelays.HighDelay,\n                    startTask: false,\n                    runNow: false,\n                    skipIteration: 0);\n            }\n\n            backtestingSaveSnapshotsTimedTask = tasksService.AddTask(\n                name: nameof(BacktestingSaveSnapshotsTimedTask),\n                task: new BacktestingSaveSnapshotsTimedTask(loggingService, healthCheckService, tradingService, signalsService, this),\n                interval: Config.SnapshotsInterval * 1000,\n                startDelay: Constants.TaskDelays.HighDelay,\n                startTask: false,\n                runNow: false,\n                skipIteration: 0);\n\n            if (Config.DeleteLogs)\n            {\n                loggingService.DeleteAllLogs();\n            }\n\n            string virtualAccountPath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath);\n            if (File.Exists(virtualAccountPath) && (Config.DeleteAccountData || !String.IsNullOrWhiteSpace(Config.CopyAccountDataPath)))\n            {\n                File.Delete(virtualAccountPath);\n            }\n\n            if (!String.IsNullOrWhiteSpace(Config.CopyAccountDataPath))\n            {\n                File.Copy(Path.Combine(Directory.GetCurrentDirectory(), Config.CopyAccountDataPath), virtualAccountPath, true);\n            }\n\n            if (Config.Replay)\n            {\n                Application.Speed = Config.ReplaySpeed;\n            }\n\n            Application.Resolve<ICoreService>().Started += OnCoreServiceStarted;\n\n            loggingService.Info(\"Backtesting service started\");\n        }\n\n        public void Stop()\n        {\n            loggingService.Info(\"Stop Backtesting service...\");\n\n            if (Config.Replay)\n            {\n                tasksService.RemoveTask(nameof(BacktestingLoadSnapshotsTimedTask), stopTask: true);\n            }\n            tasksService.RemoveTask(nameof(BacktestingSaveSnapshotsTimedTask), stopTask: true);\n\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotTaken);\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotTaken);\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotLoaded);\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotLoaded);\n\n            Application.Resolve<ICoreService>().Started -= OnCoreServiceStarted;\n\n            loggingService.Info(\"Backtesting service stopped\");\n        }\n\n        public void Complete(int skippedSignalSnapshots, int skippedTickerSnapshots)\n        {\n            loggingService.Info(\"Backtesting results:\");\n\n            double lagAmount = 0;\n            foreach (var kvp in tasksService.GetAllTasks().OrderBy(t => t.Key))\n            {\n                string taskName = kvp.Key;\n                ITimedTask task = kvp.Value;\n\n                double averageWaitTime = Math.Round(task.TotalLagTime / task.RunCount, 3);\n                if (averageWaitTime > 0) lagAmount += averageWaitTime;\n                loggingService.Info($\" [+] {taskName} Run times: {task.RunCount}, average wait time: \" + averageWaitTime);\n            }\n\n            loggingService.Info($\"Lag value: {lagAmount}. Lower the ReplaySpeed if lag value is positive.\");\n            loggingService.Info($\"Skipped signal snapshots: {skippedSignalSnapshots}\");\n            loggingService.Info($\"Skipped ticker snapshots: {skippedTickerSnapshots}\");\n\n            tradingService.SuspendTrading(forced: true);\n            signalsService.StopTrailing();\n            signalsService.Stop();\n        }\n\n        public string GetSnapshotFilePath(string snapshotEntity)\n        {\n            var date = DateTimeOffset.UtcNow;\n            return Path.Combine(\n                Directory.GetCurrentDirectory(),\n                Config.SnapshotsPath,\n                snapshotEntity,\n                date.ToString(\"yyyy-MM-dd\"),\n                date.ToString(\"HH\"),\n                date.ToString(\"mm-ss-fff\")\n            ) + \".\" + SNAPSHOT_FILE_EXTENSION;\n        }\n\n        public Dictionary<string, IEnumerable<ISignal>> GetCurrentSignals()\n        {\n            return backtestingLoadSnapshotsTimedTask.GetCurrentSignals() ?? new Dictionary<string, IEnumerable<ISignal>>();\n        }\n\n        public Dictionary<string, ITicker> GetCurrentTickers()\n        {\n            return backtestingLoadSnapshotsTimedTask.GetCurrentTickers() ?? new Dictionary<string, ITicker>();\n        }\n\n        public int GetTotalSnapshots()\n        {\n            return backtestingLoadSnapshotsTimedTask.GetTotalSnapshots();\n        }\n\n        private void OnCoreServiceStarted()\n        {\n            tasksService.GetTask<TradingTimedTask>(nameof(TradingTimedTask)).SkipIteration = Config.TradingSpeedEasing;\n            tasksService.GetTask<TradingTimedTask>(nameof(TradingTimedTask)).LoggingEnabled = false;\n            tasksService.GetTask<TradingRulesTimedTask>(nameof(TradingRulesTimedTask)).SkipIteration = Config.TradingRulesSpeedEasing;\n            tasksService.GetTask<SignalRulesTimedTask>(nameof(SignalRulesTimedTask)).SkipIteration = Config.SignalRulesSpeedEasing;\n            tasksService.GetTask<SignalRulesTimedTask>(nameof(SignalRulesTimedTask)).LoggingEnabled = false;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/Services/BacktestingSignalsService.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Signals.Base;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace IntelliTrader.Backtesting\n{\n    public class BacktestingSignalsService : ConfigrableServiceBase<SignalsConfig>, ISignalsService\n    {\n        public override string ServiceName => Constants.ServiceNames.SignalsService;\n\n        ISignalsConfig ISignalsService.Config => Config;\n\n        public IModuleRules Rules { get; private set; }\n        public ISignalRulesConfig RulesConfig { get; private set; }\n\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITasksService tasksService;\n        private readonly ITradingService tradingService;\n        private readonly IRulesService rulesService;\n        private readonly IBacktestingService backtestingService;\n\n        private SignalRulesTimedTask signalRulesTimedTask;\n        private IEnumerable<string> signalNames;\n\n        public BacktestingSignalsService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService, ITradingService tradingService, IRulesService rulesService, IBacktestingService backtestingService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tasksService = tasksService;\n            this.tradingService = tradingService;\n            this.rulesService = rulesService;\n            this.backtestingService = backtestingService;\n        }\n\n        public void Start()\n        {\n            loggingService.Info(\"Start Backtesting Signals service...\");\n\n            OnSignalRulesChanged();\n            rulesService.RegisterRulesChangeCallback(OnSignalRulesChanged);\n\n            signalRulesTimedTask = tasksService.AddTask(\n                name: nameof(SignalRulesTimedTask),\n                task: new SignalRulesTimedTask(loggingService, healthCheckService, tradingService, rulesService, this),\n                interval: RulesConfig.CheckInterval * 1000 / Application.Speed,\n                startDelay: Constants.TaskDelays.LowDelay,\n                startTask: false,\n                runNow: false,\n                skipIteration: 0);\n\n            loggingService.Info(\"Backtesting Signals service started\");\n        }\n\n        public void Stop()\n        {\n            loggingService.Info(\"Stop Backtesting Signals service...\");\n\n            tasksService.RemoveTask(nameof(SignalRulesTimedTask), stopTask: true);\n\n            rulesService.UnregisterRulesChangeCallback(OnSignalRulesChanged);\n\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.SignalRulesProcessed);\n\n            loggingService.Info(\"Backtesting Signals service stopped\");\n        }\n\n        public void ProcessPair(string pair, Dictionary<string, ISignal> signals)\n        {\n            IEnumerable<IRule> enabledRules = Rules.Entries.Where(r => r.Enabled);\n            foreach (IRule rule in enabledRules)\n            {\n                signalRulesTimedTask.ProcessRule(rule, signals, pair, signalRulesTimedTask.GetExcludedPairs(), GetGlobalRating());\n            }\n        }\n\n        public void StopTrailing()\n        {\n            signalRulesTimedTask.StopTrailing();\n        }\n\n        public List<string> GetTrailingSignals()\n        {\n            return signalRulesTimedTask.GetTrailingSignals();\n        }\n\n        public IEnumerable<ISignalTrailingInfo> GetTrailingInfo(string pair)\n        {\n            return signalRulesTimedTask.GetTrailingInfo(pair);\n        }\n\n        public IEnumerable<string> GetSignalNames()\n        {\n            if (signalNames == null)\n            {\n                signalNames = backtestingService.GetCurrentSignals().Values.SelectMany(val => val.Select(s => s.Name)).Distinct().ToList();\n            }\n            return signalNames;\n\n        }\n\n        public IEnumerable<ISignal> GetAllSignals()\n        {\n            return GetSignalsByName(null);\n        }\n\n        public IEnumerable<ISignal> GetSignalsByName(string signalName)\n        {\n            IEnumerable<ISignal> allSignals = backtestingService.GetCurrentSignals().SelectMany(s => s.Value);\n            if (signalName == null)\n            {\n                return allSignals;\n            }\n            else\n            {\n                return allSignals.Where(s => s.Name == signalName);\n            }\n        }\n\n        public IEnumerable<ISignal> GetSignalsByPair(string pair)\n        {\n            if (backtestingService.GetCurrentSignals().TryGetValue(pair, out IEnumerable<ISignal> signalsByPair))\n            {\n                return signalsByPair;\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        public ISignal GetSignal(string pair, string signalName)\n        {\n            return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair);\n        }\n\n        public double? GetRating(string pair, string signalName)\n        {\n            return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating;\n        }\n\n        public double? GetRating(string pair, IEnumerable<string> signalNames)\n        {\n            if (signalNames != null && signalNames.Count() > 0)\n            {\n                double ratingSum = 0;\n\n                foreach (var signalName in signalNames)\n                {\n                    var rating = GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating;\n                    if (rating != null)\n                    {\n                        ratingSum += rating.Value;\n                    }\n                    else\n                    {\n                        return null;\n                    }\n                }\n\n                return Math.Round(ratingSum / signalNames.Count(), 8);\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        public double? GetGlobalRating()\n        {\n            try\n            {\n                double ratingSum = 0;\n                double ratingCount = 0;\n\n                var currentSignals = backtestingService.GetCurrentSignals();\n                if (currentSignals != null)\n                {\n                    var signalGroups = currentSignals.Values.SelectMany(s => s).GroupBy(s => s.Name);\n                    foreach (var signalGroup in signalGroups)\n                    {\n                        if (Config.GlobalRatingSignals.Contains(signalGroup.Key))\n                        {\n                            double? averageRating = signalGroup.Average(s => s.Rating);\n                            if (averageRating != null)\n                            {\n                                ratingSum += averageRating.Value;\n                                ratingCount++;\n                            }\n                        }\n                    }\n                }\n\n                if (ratingCount > 0)\n                {\n                    return Math.Round(ratingSum / ratingCount, 8);\n                }\n                else\n                {\n                    return null;\n                }\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error(\"Unable to get global rating\", ex);\n                return null;\n            }\n        }\n\n        private void OnSignalRulesChanged()\n        {\n            Rules = rulesService.GetRules(ServiceName);\n            RulesConfig = Rules.GetConfiguration<SignalRulesConfig>();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/TimedTasks/BacktestingLoadSnapshotsTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing ZeroFormatter;\n\nnamespace IntelliTrader.Backtesting\n{\n    internal class BacktestingLoadSnapshotsTimedTask : HighResolutionTimedTask\n    {\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITradingService tradingService;\n        private readonly IBacktestingService backtestingService;\n\n        private Queue<string> allSignalSnapshotPaths;\n        private Queue<string> allTickerSnapshotPaths;\n        private Dictionary<string, IEnumerable<ISignal>> currentSignals;\n        private Dictionary<string, ITicker> currentTickers;\n        private Stopwatch backtestingTimer;\n\n        private int totalSignalSnapshots;\n        private int totalTickerSnapshots;\n        public int loadedSignalSnapshots;\n        public int loadedTickerSnapshots;\n        private bool isCompleted;\n\n        public object Config { get; }\n\n        public BacktestingLoadSnapshotsTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService, IBacktestingService backtestingService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tradingService = tradingService;\n            this.backtestingService = backtestingService;\n\n            PopulateSnapshotPaths();\n        }\n\n        protected override void Run()\n        {\n            if (!isCompleted)\n            {\n                LoadNextSnapshots();\n            }\n        }\n\n        public Dictionary<string, IEnumerable<ISignal>> GetCurrentSignals()\n        {\n            lock (backtestingService.SyncRoot)\n            {\n                return currentSignals;\n            }\n        }\n\n        public Dictionary<string, ITicker> GetCurrentTickers()\n        {\n            lock (backtestingService.SyncRoot)\n            {\n                return currentTickers;\n            }\n        }\n\n        public int GetTotalSnapshots()\n        {\n            return totalSignalSnapshots;\n        }\n\n        private void LoadNextSnapshots()\n        {\n            lock (backtestingService.SyncRoot)\n            {\n                if (loadedSignalSnapshots == 0 && loadedTickerSnapshots == 0)\n                {\n                    loggingService.Info($\"<<<--- Backtesting started. Total signals snapshots: {totalSignalSnapshots}, Total tickers snapshots: {totalTickerSnapshots} --->>>\");\n                    backtestingTimer = Stopwatch.StartNew();\n                }\n\n                if (allSignalSnapshotPaths.TryDequeue(out string currentSignalsSnapshotPath))\n                {\n                    try\n                    {\n                        byte[] currentSignalsSnapshotBytes = File.ReadAllBytes(currentSignalsSnapshotPath);\n                        IEnumerable<ISignal> data = ZeroFormatterSerializer.Deserialize<IEnumerable<SignalData>>(currentSignalsSnapshotBytes).Select(s => s.ToSignal()).ToList();\n                        currentSignals = data.GroupBy(s => s.Pair).ToDictionary(s => s.Key, s => s.AsEnumerable());\n                        loadedSignalSnapshots++;\n\n                        var currentSignalsSnapshotFile = currentSignalsSnapshotPath.Substring(currentSignalsSnapshotPath.Length - 27);\n                        currentSignalsSnapshotFile = currentSignalsSnapshotFile.Replace('\\\\', '-').Replace('/', '-');\n                        if (backtestingService.Config.ReplayOutput && loadedSignalSnapshots % 100 == 0)\n                        {\n                            loggingService.Info($\"<<<--- ({loadedSignalSnapshots}/{totalSignalSnapshots}) Load signals snapshot file: {currentSignalsSnapshotFile} --->>>\");\n                        }\n                        healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotLoaded, $\"File: {currentSignalsSnapshotFile}\");\n                    }\n                    catch (Exception ex)\n                    {\n                        loggingService.Error($\"<<<--- Unable to load signals snapshot file: {currentSignalsSnapshotPath} --->>>\", ex);\n                    }\n                }\n\n                if (allTickerSnapshotPaths.TryDequeue(out string currentTickersSnapshotPath))\n                {\n                    try\n                    {\n                        byte[] currentTickersSnapshotBytes = File.ReadAllBytes(currentTickersSnapshotPath);\n                        IEnumerable<ITicker> data = ZeroFormatterSerializer.Deserialize<IEnumerable<TickerData>>(currentTickersSnapshotBytes).Select(t => t.ToTicker()).ToList();\n                        currentTickers = data.ToDictionary(t => t.Pair, t => t);\n                        loadedTickerSnapshots++;\n\n                        var currentTickersSnapshotFile = currentTickersSnapshotPath.Substring(currentTickersSnapshotPath.Length - 27);\n                        currentTickersSnapshotFile = currentTickersSnapshotFile.Replace('\\\\', '-').Replace('/', '-');\n                        if (backtestingService.Config.ReplayOutput && loadedTickerSnapshots % 100 == 0)\n                        {\n                            loggingService.Info($\"<<<--- ({loadedTickerSnapshots}/{totalTickerSnapshots}) Load tickers snapshot file: {currentTickersSnapshotFile} --->>>\");\n                        }\n                        healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotLoaded, $\"File: {currentTickersSnapshotFile}\");\n                    }\n                    catch (Exception ex)\n                    {\n                        loggingService.Error($\"<<<--- Unable to load tickers snapshot file: {currentTickersSnapshotPath} --->>>\", ex);\n                    }\n                }\n\n                if (currentSignalsSnapshotPath == null && currentTickersSnapshotPath == null)\n                {\n                    isCompleted = true;\n                    backtestingTimer.Stop();\n                    loggingService.Info($\"<<<--- Backtesting finished in {Math.Round(backtestingTimer.Elapsed.TotalSeconds)} seconds --->>>\");\n                    backtestingService.Complete(totalSignalSnapshots - loadedSignalSnapshots, totalTickerSnapshots - loadedTickerSnapshots);\n                }\n            }\n        }\n\n        private void PopulateSnapshotPaths()\n        {\n            var signalsSnapshotPath = Path.Combine(Directory.GetCurrentDirectory(), backtestingService.Config.SnapshotsPath, Constants.SnapshotEntities.Signals);\n            if (Directory.Exists(signalsSnapshotPath))\n            {\n                var files = Directory.EnumerateFiles(signalsSnapshotPath, \"*.\" + BacktestingService.SNAPSHOT_FILE_EXTENSION, SearchOption.AllDirectories);\n                allSignalSnapshotPaths = new Queue<string>(files.Take(backtestingService.Config.ReplayEndIndex ?? files.Count()).Skip(backtestingService.Config.ReplayStartIndex ?? 0));\n            }\n            else\n            {\n                allSignalSnapshotPaths = new Queue<string>();\n            }\n            totalSignalSnapshots = allSignalSnapshotPaths.Count;\n\n            var tickersSnapshotPath = Path.Combine(Directory.GetCurrentDirectory(), backtestingService.Config.SnapshotsPath, Constants.SnapshotEntities.Tickers);\n            if (Directory.Exists(tickersSnapshotPath))\n            {\n                var files = Directory.EnumerateFiles(tickersSnapshotPath, \"*.\" + BacktestingService.SNAPSHOT_FILE_EXTENSION, SearchOption.AllDirectories);\n                allTickerSnapshotPaths = new Queue<string>(files.Take(backtestingService.Config.ReplayEndIndex ?? files.Count()).Skip(backtestingService.Config.ReplayStartIndex ?? 0));\n            }\n            else\n            {\n                allTickerSnapshotPaths = new Queue<string>();\n            }\n            totalTickerSnapshots = allTickerSnapshotPaths.Count;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Backtesting/TimedTasks/BacktestingSaveSnapshotsTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System.IO;\nusing System.Linq;\nusing ZeroFormatter;\n\nnamespace IntelliTrader.Backtesting\n{\n    internal class BacktestingSaveSnapshotsTimedTask : HighResolutionTimedTask\n    {\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITradingService tradingService;\n        private readonly ISignalsService signalsService;\n        private readonly IBacktestingService backtestingService;\n\n        public BacktestingSaveSnapshotsTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService, ISignalsService signalsService, IBacktestingService backtestingService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tradingService = tradingService;\n            this.signalsService = signalsService;\n            this.backtestingService = backtestingService;\n        }\n\n        protected override void Run()\n        {\n            if (backtestingService.Config.Enabled && !backtestingService.Config.Replay)\n            {\n                TakeSignalsSnapshot();\n                TakeTickersSnapshot();\n            }\n        }\n\n        private void TakeSignalsSnapshot()\n        {\n            var signals = signalsService.GetAllSignals().Select(s => SignalData.FromSignal(s));\n\n            byte[] signalBytes = ZeroFormatterSerializer.Serialize(signals);\n            string signalsSnapshotFilePath = backtestingService.GetSnapshotFilePath(Constants.SnapshotEntities.Signals);\n            var signalsSnapshotFile = new FileInfo(signalsSnapshotFilePath);\n            signalsSnapshotFile.Directory.Create();\n            File.WriteAllBytes(signalsSnapshotFilePath, signalBytes);\n\n            healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotTaken, $\"Signals: {signals.Count()}\");\n        }\n\n        private void TakeTickersSnapshot()\n        {\n            var tickers = tradingService.Exchange.GetTickers().Select(t => TickerData.FromTicker(t));\n\n            byte[] tickerBytes = ZeroFormatterSerializer.Serialize(tickers);\n            string tickersSnapshotFilePath = backtestingService.GetSnapshotFilePath(Constants.SnapshotEntities.Tickers);\n            var tickersSnapshotFile = new FileInfo(tickersSnapshotFilePath);\n            tickersSnapshotFile.Directory.Create();\n            File.WriteAllBytes(tickersSnapshotFilePath, tickerBytes);\n\n            healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotTaken, $\"Tickers: {tickers.Count()}\");\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/AppModule.cs",
    "content": "﻿using Autofac;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<TasksService>().As<ITasksService>().SingleInstance();\n            builder.RegisterType<HealthCheckService>().As<IHealthCheckService>().SingleInstance();\n            builder.RegisterType<CoreService>().As<ICoreService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.CoreService).SingleInstance();\n            builder.RegisterType<LoggingService>().As<ILoggingService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.LoggingService).SingleInstance();\n            builder.RegisterType<NotificationService>().As<INotificationService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.NotificationService).SingleInstance();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Application.cs",
    "content": "﻿using Autofac;\nusing Autofac.Core;\nusing System;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text.RegularExpressions;\n\nnamespace IntelliTrader.Core\n{\n    public class Application\n    {\n        public readonly static IConfigProvider ConfigProvider = new ConfigProvider();\n\n        public static double Speed { get; set; } = 1;\n\n        public static ILifetimeScope Container\n        {\n            get\n            {\n                RegisterComponents();\n                return container;\n            }\n        }\n\n        private static IContainer container;\n\n        public static void RegisterComponents(bool repos = true, bool queries = true, bool mappers = true)\n        {\n            if (Application.container == null)\n            {\n                var builder = new ContainerBuilder();\n\n                var assemblyPattern = new Regex($\"{nameof(IntelliTrader)}.*.dll\");\n                var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => assemblyPattern.IsMatch(Path.GetFileName(a.Location)));\n                var dynamicAssembliesPath = new Uri(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location)).LocalPath;\n                var dynamicAssemblies = Directory.EnumerateFiles(dynamicAssembliesPath, \"*.dll\", SearchOption.AllDirectories)\n                           .Where(filename => assemblyPattern.IsMatch(Path.GetFileName(filename)) &&\n                           !loadedAssemblies.Any(a => Path.GetFileName(a.Location) == Path.GetFileName(filename)));\n\n                var allAssemblies = loadedAssemblies.Concat(dynamicAssemblies.Select(Assembly.LoadFrom)).Distinct();\n\n                builder.RegisterAssemblyModules(allAssemblies.ToArray());\n                Application.container = builder.Build();\n            }\n        }\n\n        public static TService Resolve<TService>(params Parameter[] parameters) where TService : class\n        {\n            return Container.Resolve<TService>(parameters);\n        }\n\n        public static TService ResolveNamed<TService>(string name, params Parameter[] parameters) where TService : class\n        {\n            return Container.ResolveNamed<TService>(name, parameters);\n        }\n\n        public static TService ResolveOptional<TService>(params Parameter[] parameters) where TService : class\n        {\n            return Container.ResolveOptional<TService>(parameters);\n        }\n\n        public static TService ResolveOptionalNamed<TService>(string name, params Parameter[] parameters) where TService : class\n        {\n            return Container.ResolveOptionalNamed<TService>(name, parameters);\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/IntelliTrader.Core.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n    <AssemblyVersion>1.1.0.0</AssemblyVersion>\n    <FileVersion>1.1.0.0</FileVersion>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Autofac\" Version=\"4.8.1\" />\n    <PackageReference Include=\"Microsoft.Extensions.Configuration\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Microsoft.Extensions.Configuration.Binder\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Microsoft.Extensions.Configuration.EnvironmentVariables\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Microsoft.Extensions.Configuration.Json\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Serilog\" Version=\"2.7.1\" />\n    <PackageReference Include=\"Serilog.Enrichers.Environment\" Version=\"2.1.2\" />\n    <PackageReference Include=\"Serilog.Filters.Expressions\" Version=\"2.0.0\" />\n    <PackageReference Include=\"Serilog.Settings.Configuration\" Version=\"2.6.1\" />\n    <PackageReference Include=\"Serilog.Sinks.Console\" Version=\"3.1.1\" />\n    <PackageReference Include=\"Serilog.Sinks.RollingFile\" Version=\"3.3.0\" />\n    <PackageReference Include=\"Telegram.Bot\" Version=\"14.6.0\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/IBacktestingConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IBacktestingConfig\n    {\n        bool Enabled { get; }\n        bool Replay { get; }\n        bool ReplayOutput { get; }\n        double ReplaySpeed { get; }\n        int? ReplayStartIndex { get; }\n        int? ReplayEndIndex { get; }\n        bool DeleteLogs { get; }\n        bool DeleteAccountData { get; }\n        string CopyAccountDataPath { get; }\n        int TradingSpeedEasing { get; }\n        int TradingRulesSpeedEasing { get; }\n        int SignalRulesSpeedEasing { get; }\n        int SnapshotsInterval { get; }\n        string SnapshotsPath { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/IConfigProvider.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IConfigProvider\n    {\n        string GetSectionJson(string sectionName);\n        void SetSectionJson(string sectionName, string definition);\n        IConfigurationSection GetSection(string sectionName, Action<IConfigurationSection> onChange = null);\n        T GetSection<T>(string sectionName, Action<T> onChange = null);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/ICoreConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ICoreConfig\n    {\n        bool DebugMode { get; }\n        bool PasswordProtected { get; }\n        string Password { get; }\n        string InstanceName { get; }\n        double TimezoneOffset { get; }\n        bool HealthCheckEnabled { get; set; }\n        double HealthCheckInterval { get; }\n        double HealthCheckSuspendTradingTimeout { get; }\n        int HealthCheckFailuresToRestartServices { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/ILoggingConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ILoggingConfig\n    {\n        bool Enabled { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/INotificationConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface INotificationConfig\n    {\n        bool Enabled { get; }\n        bool TelegramEnabled { get; }\n        string TelegramBotToken { get; }\n        long TelegramChatId { get; }\n        bool TelegramAlertsEnabled { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/IRulesConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IRulesConfig\n    {\n        IEnumerable<IModuleRules> Modules { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/ISignalsConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISignalsConfig\n    {\n        bool Enabled { get; }\n        IEnumerable<string> GlobalRatingSignals { get; }\n        IEnumerable<ISignalDefinition> Definitions { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/ITradingConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITradingConfig : IBuyConfig, IBuyDCAConfig, ISellConfig, ISellDCAConfig\n    {\n        bool Enabled { get; }\n        string Market { get; }\n        string Exchange { get; }\n        int MaxPairs { get; }\n        decimal MinCost { get; }\n        List<string> ExcludedPairs { get; }\n        TradePriceType TradePriceType { get; set; }\n\n        bool RepeatLastDCALevel { get; }\n        List<DCALevel> DCALevels { get; }\n\n        double TradingCheckInterval { get; }\n        double AccountRefreshInterval { get; }\n        decimal AccountInitialBalance { get; }\n        DateTimeOffset AccountInitialBalanceDate { get; }\n        string AccountFilePath { get; }\n\n        bool VirtualTrading { get; }\n        decimal VirtualTradingFees { get; }\n        decimal VirtualAccountInitialBalance { get; }\n        string VirtualAccountFilePath { get; }\n\n        ITradingConfig Clone();\n\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Configs/IWebConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IWebConfig\n    {\n        bool Enabled { get; }\n        bool DebugMode { get; }\n        bool ReadOnlyMode { get; }\n        int Port { get; }\n        bool SSLEnabled { get; }\n        string SSLCertPath { get; set; }\n        string SSLCertPassword { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Exchange/ITicker.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITicker\n    {\n        string Pair { get; }\n        decimal BidPrice { get; }\n        decimal AskPrice { get; }\n        decimal LastPrice { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/IHealthCheck.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IHealthCheck\n    {\n        string Name { get; }\n        string Message { get; }\n        DateTimeOffset LastUpdated { get; }\n        bool Failed { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Rules/IModuleRules.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IModuleRules\n    {\n        string Module { get; }\n        IConfigurationSection Configuration { get; }\n        IEnumerable<IRule> Entries { get; }\n\n        T GetConfiguration<T>();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Rules/IRule.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IRule\n    {\n        bool Enabled { get; }\n        string Name { get; }\n        RuleAction Action { get; }\n        IEnumerable<IRuleCondition> Conditions { get; }\n        IRuleTrailing Trailing { get; }\n        IConfigurationSection Modifiers { get; }\n        T GetModifiers<T>();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Rules/IRuleCondition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IRuleCondition\n    {\n        string Signal { get; }\n        decimal? MinPrice { get; }\n        decimal? MaxPrice { get; }\n        decimal? MinSpread { get; }\n        decimal? MaxSpread { get; }\n        long? MinVolume { get; }\n        long? MaxVolume { get; }\n        double? MinVolumeChange { get; }\n        double? MaxVolumeChange { get; }\n        decimal? MinPriceChange { get; }\n        decimal? MaxPriceChange { get; }\n        double? MinRating { get; }\n        double? MaxRating { get; }\n        double? MinRatingChange { get; }\n        double? MaxRatingChange { get; }\n        double? MinVolatility { get; }\n        double? MaxVolatility { get; }\n        double? MinGlobalRating { get; }\n        double? MaxGlobalRating { get; }\n\n        decimal? MinArbitrage { get; }\n        decimal? MaxArbitrage { get; }\n        ArbitrageMarket? ArbitrageMarket { get; }\n        ArbitrageType? ArbitrageType { get; }\n\n        List<string> Pairs { get; }\n        List<string> NotPairs { get; }\n\n        double? MinAge { get; }\n        double? MaxAge { get; }\n        double? MinLastBuyAge { get; }\n        double? MaxLastBuyAge { get; }\n        decimal? MinMargin { get; }\n        decimal? MaxMargin { get; }\n        decimal? MinMarginChange { get; }\n        decimal? MaxMarginChange { get; }\n        decimal? MinAmount { get; set; }\n        decimal? MaxAmount { get; set; }\n        decimal? MinCost { get; }\n        decimal? MaxCost { get; }\n        int? MinDCALevel { get; }\n        int? MaxDCALevel { get; }\n        List<string> SignalRules { get; }\n        List<string> NotSignalRules { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Rules/IRuleTrailing.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IRuleTrailing\n    {\n        bool Enabled { get; }\n        int MinDuration { get; }\n        int MaxDuration { get; }\n        IEnumerable<IRuleCondition> StartConditions { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Rules/ISignalRulesConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISignalRulesConfig\n    {\n        RuleProcessingMode ProcessingMode { get; }\n        double CheckInterval { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Rules/RuleProcessingMode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum RuleProcessingMode\n    {\n        FirstMatch,\n        AllMatches\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/Base/IConfigurableService.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IConfigurableService : INamedService\n    {\n        IConfigurationSection RawConfig { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/Base/INamedService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface INamedService\n    {\n        string ServiceName { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/IBacktestingService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IBacktestingService : IConfigurableService\n    {\n        IBacktestingConfig Config { get; }\n        object SyncRoot { get; }\n        void Start();\n        void Stop();\n        void Complete(int skippedSignalSnapshots, int skippedTickerSnapshots);\n        string GetSnapshotFilePath(string snapshotEntity);\n        Dictionary<string, IEnumerable<ISignal>> GetCurrentSignals();\n        Dictionary<string, ITicker> GetCurrentTickers();\n        int GetTotalSnapshots();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/ICoreService.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\n\nnamespace IntelliTrader.Core\n{\n    public interface ICoreService : IConfigurableService\n    {\n        event Action Started;\n        ICoreConfig Config { get; }\n        string Version { get; }\n        void Start();\n        void Stop();\n        void Restart();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/IExchangeService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader.Core\n{\n    public interface IExchangeService : IConfigurableService\n    {\n        void Start(bool virtualTrading);\n        void Stop();\n        IOrderDetails PlaceOrder(IOrder order);\n        decimal ClampOrderAmount(string pair, decimal amount);\n        decimal ClampOrderPrice(string pair, decimal price);\n        void ConnectTickersWebsocket();\n        void DisconnectTickersWebsocket();\n        IEnumerable<ITicker> GetTickers();\n        IEnumerable<string> GetMarkets();\n        IEnumerable<string> GetMarketPairs(string market);\n        Dictionary<string, decimal> GetAvailableAmounts();\n        IEnumerable<IOrderDetails> GetTrades(string pair);\n        decimal GetPrice(string pair, TradePriceType priceType);\n        decimal GetPriceSpread(string pair);\n        Arbitrage GetArbitrage(string pair, string tradingMarket, List<ArbitrageMarket> arbitrageMarkets = null, ArbitrageType? arbitrageType = null);\n        string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket);\n        string GetPairMarket(string pair);\n        string ChangeMarket(string pair, string market);\n        decimal ConvertPrice(string pair, decimal price, string market, TradePriceType priceType);\n        TimeSpan GetTimeElapsedSinceLastTickersUpdate();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/IHealthCheckService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IHealthCheckService\n    {\n        void Start();\n        void Stop();\n        void UpdateHealthCheck(string name, string message = null, bool failed = false);\n        void RemoveHealthCheck(string name);\n        IEnumerable<IHealthCheck> GetHealthChecks();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/ILoggingService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ILoggingService : IConfigurableService\n    {\n        void Debug(string message, Exception exception = null);\n        void Debug(string message, params object[] propertyValues);\n        void Error(string message, Exception exception = null);\n        void Error(string message, params object[] propertyValues);\n        void Fatal(string message, Exception exception = null);\n        void Fatal(string message, params object[] propertyValues);\n        void Info(string message, Exception exception = null);\n        void Info(string message, params object[] propertyValues);\n        void Verbose(string message, Exception exception = null);\n        void Verbose(string message, params object[] propertyValues);\n        void Warning(string message, Exception exception = null);\n        void Warning(string message, params object[] propertyValues);\n        void DeleteAllLogs();\n        string[] GetLogEntries();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/INotificationService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface INotificationService\n    {\n        INotificationConfig Config { get; }\n        void Start();\n        void Stop();\n        void Notify(string message);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/IOrderingService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IOrderingService\n    {\n        IOrderDetails PlaceBuyOrder(BuyOptions options);\n        IOrderDetails PlaceSellOrder(SellOptions options);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/IRulesService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IRulesService : IConfigurableService\n    {\n        IRulesConfig Config { get; }\n        IModuleRules GetRules(string module);\n        bool CheckConditions(IEnumerable<IRuleCondition> conditions, Dictionary<string, ISignal> signals, double? globalRating, string pair, ITradingPair tradingPair);\n        void RegisterRulesChangeCallback(Action callback);\n        void UnregisterRulesChangeCallback(Action callback);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/ISignalsService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISignalsService : IConfigurableService\n    {\n        ISignalsConfig Config { get; }\n        IModuleRules Rules { get; }\n        ISignalRulesConfig RulesConfig { get; }\n        void Start();\n        void Stop();\n        void ProcessPair(string pair, Dictionary<string, ISignal> signals);\n        void StopTrailing();\n        List<string> GetTrailingSignals();\n        IEnumerable<ISignalTrailingInfo> GetTrailingInfo(string pair);\n        IEnumerable<string> GetSignalNames();\n        IEnumerable<ISignal> GetAllSignals();\n        IEnumerable<ISignal> GetSignalsByName(string signalName);\n        IEnumerable<ISignal> GetSignalsByPair(string pair);\n        double? GetRating(string pair, string signalName);\n        double? GetRating(string pair, IEnumerable<string> signalNames);\n        double? GetGlobalRating();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/ITasksService.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITasksService\n    {\n        T AddTask<T>(string name, T task, double interval, double startDelay = 0, bool startTask = true, bool runNow = false, int skipIteration = 0) where T : ITimedTask;\n        void RemoveTask(string name, bool stopTask = true);\n        void StartAllTasks();\n        void StopAllTasks();\n        void RemoveAllTasks();\n        ITimedTask GetTask(string name);\n        T GetTask<T>(string name);\n        IEnumerable<KeyValuePair<string, ITimedTask>> GetAllTasks();\n        void SetUnhandledExceptionHandler(UnhandledExceptionEventHandler handler);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/ITradingService.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITradingService : IConfigurableService\n    {\n        ITradingConfig Config { get; }\n        IModuleRules Rules { get; }\n        IExchangeService Exchange { get; }\n        ITradingAccount Account { get; }\n        ConcurrentStack<IOrderDetails> OrderHistory { get; }\n        bool IsTradingSuspended { get; }\n        void Start();\n        void Stop();\n        void ResumeTrading(bool forced = false);\n        void SuspendTrading(bool forced = false);\n        IPairConfig GetPairConfig(string pair);\n        void ReapplyTradingRules();\n        void Buy(BuyOptions options);\n        void Sell(SellOptions options);\n        void Swap(SwapOptions options);\n        void Arbitrage(ArbitrageOptions options);\n        bool CanBuy(BuyOptions options, out string message);\n        bool CanSell(SellOptions options, out string message);\n        bool CanSwap(SwapOptions options, out string message);\n        bool CanArbitrage(ArbitrageOptions options, out string message);\n        decimal GetPrice(string pair, TradePriceType? priceType = null, bool normalize = true);\n        decimal CalculateOrderFees(IOrderDetails order);\n        bool IsNormalizedPair(string pair);\n        string NormalizePair(string pair);\n        void LogOrder(IOrderDetails order);\n        List<string> GetTrailingBuys();\n        List<string> GetTrailingSells();\n        void StopTrailingBuy(string pair);\n        void StopTrailingSell(string pair);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Services/IWebService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IWebService\n    {\n        IWebConfig Config { get; }\n        void Start();\n        void Stop();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Signals/ISignal.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISignal\n    {\n        string Name { get; }\n        string Pair { get; }\n        long? Volume { get; }\n        double? VolumeChange { get; set; }\n        decimal? Price { get; }\n        decimal? PriceChange { get; }\n        double? Rating { get; }\n        double? RatingChange { get; }\n        double? Volatility { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Signals/ISignalDefinition.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISignalDefinition\n    {\n        string Name { get; }\n        string Receiver { get; }\n        IConfigurationSection Configuration { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Signals/ISignalTrailingInfo.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISignalTrailingInfo\n    {\n        IRule Rule { get; }\n        DateTimeOffset StartTime { get; }\n        double Duration { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Tasks/ITimedTask.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITimedTask\n    {\n        event UnhandledExceptionEventHandler UnhandledException;\n\n        double StartDelay { get; set; }\n        double Interval { get; set; }\n        int SkipIteration { get; set; }\n        Stopwatch Stopwatch { get; set; }\n        bool IsRunning { get; }\n        long RunCount { get; }\n        double TotalRunTime { get; }\n        double TotalLagTime { get; }\n\n        void Start();\n        void Stop();\n        void Pause();\n        void Continue();\n        void RunNow();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/IBuyConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IBuyConfig\n    {\n        bool BuyEnabled { get; set; }\n        OrderType BuyType { get; }\n        decimal BuyMaxCost { get; }\n        decimal BuyMultiplier { get; }\n        decimal BuyMinBalance { get; }\n        double BuySamePairTimeout { get; }\n        decimal BuyTrailing { get; }\n        decimal BuyTrailingStopMargin { get; }\n        BuyTrailingStopAction BuyTrailingStopAction { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/IBuyDCAConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IBuyDCAConfig\n    {\n        bool BuyDCAEnabled { get; set; }\n        decimal BuyDCAMultiplier { get; }\n        decimal BuyDCAMinBalance { get; }\n        double BuyDCASamePairTimeout { get; }\n        decimal BuyDCATrailing { get; }\n        decimal BuyDCATrailingStopMargin { get; }\n        BuyTrailingStopAction BuyDCATrailingStopAction { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/IOrder.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IOrder\n    {\n        OrderSide Side { get; }\n        OrderType Type { get; }\n        DateTimeOffset Date { get; }\n        string Pair { get; }\n        decimal Amount { get; }\n        decimal Price { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/IOrderDetails.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IOrderDetails\n    {\n        bool IsNormalized { get; }\n        OrderSide Side { get; }\n        OrderResult Result { get; }\n        DateTimeOffset Date { get; }\n        string OrderId { get; }\n        string Pair { get; }\n        string OriginalPair { get; }\n        string Message { get; }\n        decimal Amount { get; }\n        decimal AmountFilled { get; }\n        decimal Price { get; }\n        decimal AveragePrice { get; }\n        decimal Fees { get; }\n        string FeesCurrency { get; }\n        decimal Cost { get; }\n        OrderMetadata Metadata { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/IPairConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface IPairConfig : IBuyConfig, ISellConfig\n    {\n        IEnumerable<string> Rules { get; }\n\n        int MaxPairs { get; }\n\n        bool SwapEnabled { get; }\n        List<string> SwapSignalRules { get; }\n        int SwapTimeout { get; }\n\n        bool ArbitrageEnabled { get; }\n        List<ArbitrageMarket> ArbitrageMarkets { get; }\n        ArbitrageType? ArbitrageType { get; }\n        decimal? ArbitrageBuyMultiplier { get; }\n        decimal? ArbitrageSellMultiplier { get; }\n        List<string> ArbitrageSignalRules { get; }\n\n        decimal? CurrentDCAMargin { get; }\n        decimal? NextDCAMargin { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/ISellConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISellConfig\n    {\n        bool SellEnabled { get; set; }\n        OrderType SellType { get; }\n        decimal SellMargin { get; }\n        decimal SellTrailing { get; }\n        decimal SellTrailingStopMargin { get; }\n        SellTrailingStopAction SellTrailingStopAction { get; }\n        bool SellStopLossEnabled { get; }\n        bool SellStopLossAfterDCA { get; }\n        double SellStopLossMinAge { get; }\n        decimal SellStopLossMargin { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/ISellDCAConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ISellDCAConfig\n    {\n        decimal SellDCAMargin { get; }\n        decimal SellDCATrailing { get; }\n        decimal SellDCATrailingStopMargin { get; }\n        SellTrailingStopAction SellDCATrailingStopAction { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/ITradeResult.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITradeResult\n    {\n        bool IsSuccessful { get; }\n        bool IsSwap { get; }\n        bool IsArbitrage { get; }\n        string Pair { get; }\n        decimal Amount { get; }\n        List<DateTimeOffset> OrderDates { get; }\n        decimal AveragePrice { get; }\n        decimal Fees { get; }\n        decimal FeesTotal { get; }\n        decimal Cost { get; }\n        DateTimeOffset SellDate { get; }\n        decimal SellPrice { get; }\n        decimal SellCost { get; }\n        decimal BalanceOffset { get; }\n        decimal Profit { get; }\n        OrderMetadata Metadata { get; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/ITradingAccount.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITradingAccount : IDisposable\n    {\n        object SyncRoot { get; }\n        void Refresh();\n        void Save();\n        void AddOrder(IOrderDetails order);\n        void AddBuyOrder(IOrderDetails order);\n        ITradeResult AddSellOrder(IOrderDetails order);\n        ITradingPair AddOrUpdatePair(IOrderDetails order, string pair, decimal feesMarketCurrency, decimal feesPairCurrency, decimal? amountOverride = null, decimal? averagePriceOverride = null);\n        IOrderDetails AddBlankOrder(string pair, decimal amount, bool includeFees = true);\n        void AddBalance(decimal balanceOffset);\n        decimal GetBalance();\n        decimal GetTotalBalance();\n        bool HasTradingPair(string pair, bool includeDust = false);\n        ITradingPair GetTradingPair(string pair, bool includeDust = false);\n        IEnumerable<ITradingPair> GetTradingPairs(bool includeDust = false);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Interfaces/Trading/ITradingPair.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public interface ITradingPair\n    {\n        string Pair { get; }\n        string FormattedName { get; }\n        int DCALevel { get; }\n        List<string> OrderIds { get; }\n        List<DateTimeOffset> OrderDates { get; }\n        decimal Amount { get; }\n        decimal AveragePrice { get; }\n        decimal Fees { get; }\n        decimal Cost { get; }\n        decimal? CostOverride { get; set; }\n        decimal CurrentCost { get; }\n        decimal CurrentPrice { get; }\n        decimal CurrentSpread { get; }\n        decimal CurrentMargin { get; }\n        double CurrentAge { get; }\n        double LastBuyAge { get; }\n        OrderMetadata Metadata { get; }\n\n        decimal GetPartialCost(decimal partialAmount);\n        void OverrideCost(decimal? costOverride);\n        void SetCurrentValues(decimal currentPrice, decimal currentSpread);\n        void SetMetadata(OrderMetadata metadata);\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Config/ConfigProvider.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.Primitives;\nusing System;\nusing System.IO;\n\nnamespace IntelliTrader.Core\n{\n    internal class ConfigProvider : IConfigProvider\n    {\n        private const string ROOT_CONFIG_DIR = \"config\";\n        private const string PATHS_CONFIG_PATH = \"paths.json\";\n        private const string PATHS_SECTION_NAME = \"Paths\";\n        private IConfigurationSection paths;\n\n        public ConfigProvider()\n        {\n            IConfigurationRoot pathsConfig = GetConfig(PATHS_CONFIG_PATH, changedPathsConfig =>\n            {\n                paths = changedPathsConfig.GetSection(PATHS_SECTION_NAME);\n            });\n            paths = pathsConfig.GetSection(PATHS_SECTION_NAME);\n        }\n\n        public string GetSectionJson(string sectionName)\n        {\n            try\n            {\n                string configPath = paths.GetValue<string>(sectionName);\n                var fullConfigPath = Path.Combine(Directory.GetCurrentDirectory(), ROOT_CONFIG_DIR, configPath);\n                return File.ReadAllText(fullConfigPath);\n            }\n            catch (Exception ex)\n            {\n                Application.Resolve<ILoggingService>().Error($\"Unable to load config section {sectionName}\", ex);\n                return null;\n            }\n        }\n\n        public void SetSectionJson(string sectionName, string definition)\n        {\n            try\n            {\n                string configPath = paths.GetValue<string>(sectionName);\n                var fullConfigPath = Path.Combine(Directory.GetCurrentDirectory(), ROOT_CONFIG_DIR, configPath);\n                File.WriteAllText(fullConfigPath, definition);\n            }\n            catch (Exception ex)\n            {\n                Application.Resolve<ILoggingService>().Error($\"Unable to save config section {sectionName}\", ex);\n            }\n        }\n\n        public T GetSection<T>(string sectionName, Action<T> onChange = null)\n        {\n            IConfigurationSection configSection = GetSection(sectionName, changedConfigSection =>\n            {\n                onChange?.Invoke(changedConfigSection.Get<T>());\n            });\n            return configSection.Get<T>();\n        }\n\n        public IConfigurationSection GetSection(string sectionName, Action<IConfigurationSection> onChange = null)\n        {\n            string configPath = paths.GetValue<string>(sectionName);\n            IConfigurationRoot configRoot = GetConfig(configPath, changedConfigRoot =>\n            {\n                onChange?.Invoke(changedConfigRoot.GetSection(sectionName));\n            });\n            return configRoot.GetSection(sectionName);\n        }\n\n        private IConfigurationRoot GetConfig(string configPath, Action<IConfigurationRoot> onChange)\n        {\n            var fullConfigPath = Path.Combine(Directory.GetCurrentDirectory(), ROOT_CONFIG_DIR);\n\n            var configBuilder = new ConfigurationBuilder()\n                 .SetBasePath(fullConfigPath)\n                 .AddJsonFile(configPath, optional: false, reloadOnChange: true)\n                 .AddEnvironmentVariables();\n\n            var configRoot = configBuilder.Build();\n            ChangeToken.OnChange(configRoot.GetReloadToken, () => onChange(configRoot));\n            return configRoot;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Config/CoreConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    internal class CoreConfig : ICoreConfig\n    {\n        public bool DebugMode { get; set; }\n        public bool PasswordProtected { get; set; }\n        public string Password { get; set; }\n        public string InstanceName { get; set; }\n        public double TimezoneOffset { get; set; }\n        public bool HealthCheckEnabled { get; set; } = true;\n        public double HealthCheckInterval { get; set; }\n        public double HealthCheckSuspendTradingTimeout { get; set; }\n        public int HealthCheckFailuresToRestartServices { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Config/LoggingConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    internal class LoggingConfig : ILoggingConfig\n    {\n        public bool Enabled { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Config/NotificationConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    internal class NotificationConfig : INotificationConfig\n    {\n        public bool Enabled { get; set; }\n        public bool TelegramEnabled { get; set; }\n        public string TelegramBotToken { get; set; }\n        public long TelegramChatId { get; set; }\n        public bool TelegramAlertsEnabled { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Constants.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public static class Constants\n    {\n        public static class ServiceNames\n        {\n            public const string CoreService = \"Core\";\n            public const string CachingService = \"Caching\";\n            public const string LoggingService = \"Logging\";\n            public const string TradingService = \"Trading\";\n            public const string ExchangeService = \"Exchange\";\n            public const string SignalsService = \"Signals\";\n            public const string RulesService = \"Rules\";\n            public const string NotificationService = \"Notification\";\n            public const string WebService = \"Web\";\n            public const string BacktestingService = \"Backtesting\";\n            public const string BacktestingExchangeService = \"BacktestingExchange\";\n            public const string BacktestingSignalsService = \"BacktestingSignals\";\n        }\n\n        public static class HealthChecks\n        {\n            public const string AccountRefreshed = \"Account refreshed\";\n            public const string TickersUpdated = \"Tickers updated\";\n            public const string TradingPairsProcessed = \"Trading pairs processed\";\n            public const string TradingViewCryptoSignalsReceived = \"TV Signals received\";\n            public const string SignalRulesProcessed = \"Signals rules processed\";\n            public const string TradingRulesProcessed = \"Trading rules processed\";\n            public const string BacktestingSignalsSnapshotTaken = \"Backtesting signals snapshot taken\";\n            public const string BacktestingTickersSnapshotTaken = \"Backtesting tickers snapshot taken\";\n            public const string BacktestingSignalsSnapshotLoaded = \"Backtesting signals snapshot loaded\";\n            public const string BacktestingTickersSnapshotLoaded = \"Backtesting tickers snapshot loaded\";\n        }\n\n        public static class SnapshotEntities\n        {\n            public const string Signals = \"signals\";\n            public const string Tickers = \"tickers\";\n        }\n\n        public static class TaskDelays\n        {\n            public const int ZeroDelay = 0;\n            public const int LowDelay = 1200;\n            public const int MidDelay = 2400;\n            public const int NormalDelay = 3300;\n            public const int HighDelay = 4700;\n        }\n\n        public static class Markets\n        {\n            public const string BTC = \"BTC\";\n            public const string ETH = \"ETH\";\n            public const string BNB = \"BNB\";\n            public const string USDT = \"USDT\";\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/HealthCheck.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    internal class HealthCheck : IHealthCheck\n    {\n        public string Name { get; set; }\n        public string Message { get; set; }\n        public DateTimeOffset LastUpdated { get; set; }\n        public bool Failed { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Logging/MemorySink.cs",
    "content": "﻿using System;\nusing System.IO;\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Formatting;\n\nnamespace IntelliTrader.Core\n{\n    internal class MemorySink : ILogEventSink\n    {\n        readonly TextWriter textWriter;\n        readonly ITextFormatter textFormatter;\n        readonly object syncRoot = new object();\n\n        public MemorySink(System.IO.TextWriter textWriter, ITextFormatter textFormatter)\n        {\n            this.textWriter = textWriter;\n            this.textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));\n        }\n\n        public void Emit(LogEvent logEvent)\n        {\n            if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));\n\n            lock (syncRoot)\n            {\n                textFormatter.Format(logEvent, textWriter);\n                textWriter.Flush();\n            }\n        }\n    }\n}"
  },
  {
    "path": "IntelliTrader.Core/Models/Logging/MemorySinkExtensions.cs",
    "content": "﻿using System;\nusing System.IO;\nusing Serilog;\nusing Serilog.Configuration;\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Formatting;\nusing Serilog.Formatting.Display;\n\nnamespace IntelliTrader.Core\n{\n    /// <summary>\n    /// Adds the WriteTo.Memory() extension method to <see cref=\"LoggerConfiguration\"/>.\n    /// </summary>\n    public static class TextWriterLoggerConfigurationExtensions\n    {\n        const string DefaultOutputTemplate = \"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}\";\n\n        /// <summary>\n        /// Write log events to the provided <see cref=\"System.IO.TextWriter\"/>.\n        /// </summary>\n        /// <param name=\"sinkConfiguration\">Logger sink configuration.</param>\n        /// <param name=\"textWriter\">The text writer to write log events to.</param>\n        /// <param name=\"outputTemplate\">Message template describing the output format.</param>\n        /// <param name=\"restrictedToMinimumLevel\">The minimum level for\n        /// events passed through the sink. Ignored when <paramref name=\"levelSwitch\"/> is specified.</param>\n        /// <param name=\"levelSwitch\">A switch allowing the pass-through minimum level\n        /// to be changed at runtime.</param>\n        /// <returns>Configuration object allowing method chaining.</returns>\n        /// <param name=\"formatProvider\">Supplies culture-specific formatting information, or null.</param>\n        /// <exception cref=\"ArgumentNullException\"></exception>\n        public static LoggerConfiguration Memory(\n            this LoggerSinkConfiguration sinkConfiguration,\n            TextWriter textWriter,\n            LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,\n            string outputTemplate = DefaultOutputTemplate,\n            IFormatProvider formatProvider = null,\n            LoggingLevelSwitch levelSwitch = null)\n        {\n            if (textWriter == null) throw new ArgumentNullException(nameof(textWriter));\n            if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate));\n\n            var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);\n            var sink = new MemorySink(textWriter, formatter);\n            return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch);\n        }\n\n        /// <summary>\n        /// Write log events to the provided <see cref=\"System.IO.TextWriter\"/>.\n        /// </summary>\n        /// <param name=\"sinkConfiguration\">Logger sink configuration.</param>\n        /// <param name=\"textWriter\">The text writer to write log events to.</param>\n        /// <param name=\"formatter\">Text formatter used by sink.</param>\n        /// /// <param name=\"restrictedToMinimumLevel\">The minimum level for\n        /// events passed through the sink. Ignored when <paramref name=\"levelSwitch\"/> is specified.</param>\n        /// <param name=\"levelSwitch\">A switch allowing the pass-through minimum level\n        /// to be changed at runtime.</param>\n        /// <exception cref=\"ArgumentNullException\"></exception>\n        public static LoggerConfiguration Memory(\n            this LoggerSinkConfiguration sinkConfiguration,\n            ITextFormatter formatter,\n            TextWriter textWriter,\n            LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,\n            LoggingLevelSwitch levelSwitch = null)\n        {\n            if (textWriter == null) throw new ArgumentNullException(nameof(textWriter));\n            if (formatter == null) throw new ArgumentNullException(nameof(formatter));\n\n            var sink = new MemorySink(textWriter, formatter);\n            return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch);\n        }\n    }\n}"
  },
  {
    "path": "IntelliTrader.Core/Models/Tasks/EqualResolutionTimedTask.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Threading;\n\nnamespace IntelliTrader.Core\n{\n    public abstract class EqualResolutionTimedTask : ITimedTask\n    {\n        /// <summary>\n        /// Raised on unhandled exception\n        /// </summary>\n        public event UnhandledExceptionEventHandler UnhandledException;\n\n        /// <summary>\n        /// Delay before starting the task in milliseconds\n        /// </summary>\n        public double StartDelay { get; set; } = 0;\n\n        /// <summary>\n        /// Periodic execution interval in milliseconds\n        /// </summary>\n        public double Interval { get; set; } = 1000;\n\n        /// <summary>\n        /// How often to skip task execution (in RunCount)\n        /// </summary>\n        public int SkipIteration { get; set; } = 0;\n\n        /// <summary>\n        /// The priority of the timer thread\n        /// </summary>\n        public ThreadPriority Priorty { get; set; } = ThreadPriority.Normal;\n\n        /// <summary>\n        /// Stopwatch to use for timing the intervals\n        /// </summary>\n        public Stopwatch Stopwatch { get; set; }\n\n        /// <summary>\n        /// Indicates whether the task is currently running\n        /// </summary>\n        public bool IsRunning { get; private set; }\n\n        /// <summary>\n        /// Number of times the task has been run\n        /// </summary>\n        public long RunCount { get; private set; }\n\n        /// <summary>\n        /// Total time it took to run the task in milliseconds\n        /// </summary>\n        public double TotalRunTime { get; private set; }\n\n        /// <summary>\n        /// Total task run delay in milliseconds\n        /// </summary>\n        public double TotalLagTime { get; private set; }\n\n        private Thread timerThread;\n        private Stopwatch runWatch;\n        private ManualResetEvent timingEvent;\n        private ManualResetEvent blockingEvent;\n\n        /// <summary>\n        /// Start the task\n        /// </summary>\n        public void Start()\n        {\n            if (!IsRunning)\n            {\n                IsRunning = true;\n                runWatch = new Stopwatch();\n                timingEvent = new ManualResetEvent(false);\n                blockingEvent = new ManualResetEvent(true);\n\n                timerThread = new Thread(() =>\n                {\n                    if (Stopwatch == null)\n                    {\n                        Stopwatch = Stopwatch.StartNew();\n                    }\n                    else if (!Stopwatch.IsRunning)\n                    {\n                        Stopwatch.Restart();\n                    }\n\n                    long startTime = Stopwatch.ElapsedMilliseconds;\n                    while (IsRunning)\n                    {\n                        blockingEvent.WaitOne();\n                        long elapsedTime = Stopwatch.ElapsedMilliseconds;\n                        double nextRunTime = RunCount * Interval + StartDelay + TotalLagTime + startTime;\n                        double waitTime = nextRunTime - elapsedTime;\n                        if (waitTime > 0)\n                        {\n                            if (timingEvent.WaitOne((int)(waitTime)))\n                            {\n                                break;\n                            }\n                        }\n\n                        if (SkipIteration == 0 || RunCount % SkipIteration != 0)\n                        {\n                            runWatch.Restart();\n                            SafeRun();\n                            long runTime = runWatch.ElapsedMilliseconds;\n                            TotalLagTime += (runTime > Interval) ? (runTime - Interval) : 0;\n                            TotalRunTime += runTime;\n                        }\n                        RunCount++;\n                    }\n                });\n\n                timerThread.Priority = Priorty;\n                timerThread.Start();\n            }\n        }\n\n        /// <summary>\n        /// Stop the task\n        /// </summary>\n        public void Stop()\n        {\n            Stop(true);\n        }\n\n        /// <summary>\n        /// Stop the task\n        /// </summary>\n        /// <remarks>\n        /// This function is waiting an executing thread (unless join is set to false).\n        /// </remarks>\n        public void Stop(bool terminateThread)\n        {\n            if (IsRunning)\n            {\n                IsRunning = false;\n                timingEvent.Set();\n                blockingEvent.Set();\n                runWatch.Stop();\n\n                if (!terminateThread)\n                {\n                    timerThread?.Join();\n                    timerThread = null;\n                }\n\n                timingEvent.Dispose();\n            }\n        }\n\n        /// <summary>\n        /// Temporarily pause the task\n        /// </summary>\n        public void Pause()\n        {\n            blockingEvent.Reset();\n        }\n\n        /// <summary>\n        ///  Continue running the task\n        /// </summary>\n        public void Continue()\n        {\n            blockingEvent.Set();\n        }\n\n        /// <summary>\n        /// Manually run the task\n        /// </summary>\n        public void RunNow()\n        {\n            SafeRun();\n        }\n\n        /// <summary>\n        /// This method must be implemented by the child class and must contain the code\n        /// to be executed periodically.\n        /// </summary>\n        protected abstract void Run();\n\n        /// <summary>\n        /// Wrap Run method in Try/Catch\n        /// </summary>\n        private void SafeRun()\n        {\n            try\n            {\n                Run();\n            }\n            catch (Exception ex)\n            {\n                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, false));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Tasks/HighResolutionTimedTask.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Threading;\n\nnamespace IntelliTrader.Core\n{\n    public abstract class HighResolutionTimedTask : ITimedTask\n    {\n        /// <summary>\n        /// Raised on unhandled exception\n        /// </summary>\n        public event UnhandledExceptionEventHandler UnhandledException;\n\n        /// <summary>\n        /// Delay before starting the task in milliseconds\n        /// </summary>\n        public double StartDelay { get; set; } = 0;\n\n        /// <summary>\n        /// Periodic execution interval in milliseconds\n        /// </summary>\n        public double Interval { get; set; } = 1000;\n\n        /// <summary>\n        /// How often to skip task execution (in RunCount)\n        /// </summary>\n        public int SkipIteration { get; set; } = 0;\n\n        /// <summary>\n        /// The priority of the timer thread\n        /// </summary>\n        public ThreadPriority Priorty { get; set; } = ThreadPriority.Normal;\n\n        /// <summary>\n        /// Stopwatch to use for timing the intervals\n        /// </summary>\n        public Stopwatch Stopwatch { get; set; }\n\n        /// <summary>\n        /// Indicates whether the task is currently running\n        /// </summary>\n        public bool IsRunning { get; private set; }\n\n        /// <summary>\n        /// Number of times the task has been run\n        /// </summary>\n        public long RunCount { get; private set; }\n\n        /// <summary>\n        /// Total time it took to run the task in milliseconds\n        /// </summary>\n        public double TotalRunTime { get; private set; }\n\n        /// <summary>\n        /// Total task run delay in milliseconds\n        /// </summary>\n        public double TotalLagTime { get; private set; }\n\n        private Thread timerThread;\n        private Stopwatch runWatch;\n        private ManualResetEvent timingEvent;\n        private ManualResetEvent blockingEvent;\n\n        /// <summary>\n        /// Start the task\n        /// </summary>\n        public void Start()\n        {\n            if (!IsRunning)\n            {\n                IsRunning = true;\n                runWatch = new Stopwatch();\n                timingEvent = new ManualResetEvent(false);\n                blockingEvent = new ManualResetEvent(true);\n\n                timerThread = new Thread(() =>\n                {\n                    if (Stopwatch == null)\n                    {\n                        Stopwatch = Stopwatch.StartNew();\n                    }\n                    else if (!Stopwatch.IsRunning)\n                    {\n                        Stopwatch.Restart();\n                    }\n\n                    long startTime = Stopwatch.ElapsedMilliseconds;\n                    double nextRunTime = StartDelay + Interval;\n\n                    while (IsRunning)\n                    {\n                        blockingEvent.WaitOne();\n                        long elapsedTime = Stopwatch.ElapsedMilliseconds;\n                        long runningTime = elapsedTime - startTime;\n                        if (nextRunTime < runningTime) nextRunTime = runningTime;\n                        double waitTime = nextRunTime - runningTime;\n                        if (waitTime > 0)\n                        {\n                            if (timingEvent.WaitOne((int)(waitTime)))\n                            {\n                                break;\n                            }\n                        }\n\n                        if (SkipIteration == 0 || RunCount % SkipIteration != 0)\n                        {\n                            runWatch.Restart();\n                            SafeRun();\n                            long runTime = runWatch.ElapsedMilliseconds;\n                            TotalLagTime += runTime - Interval;\n                            TotalRunTime += runTime;\n                        }\n                        RunCount++;\n                        nextRunTime += Interval;\n                    }\n                });\n\n                timerThread.Priority = Priorty;\n                timerThread.Start();\n            }\n        }\n\n        /// <summary>\n        /// Stop the task\n        /// </summary>\n        public void Stop()\n        {\n            Stop(true);\n        }\n\n        /// <summary>\n        /// Stop the task\n        /// </summary>\n        /// <remarks>\n        /// This function is waiting an executing thread (unless join is set to false).\n        /// </remarks>\n        public void Stop(bool terminateThread)\n        {\n            if (IsRunning)\n            {\n                IsRunning = false;\n                timingEvent.Set();\n                blockingEvent.Set();\n                runWatch.Stop();\n\n                if (!terminateThread)\n                {\n                    timerThread?.Join();\n                    timerThread = null;\n                }\n\n                timingEvent.Dispose();\n            }\n        }\n\n        /// <summary>\n        /// Temporarily pause the task\n        /// </summary>\n        public void Pause()\n        {\n            blockingEvent.Reset();\n        }\n\n        /// <summary>\n        ///  Continue running the task\n        /// </summary>\n        public void Continue()\n        {\n            blockingEvent.Set();\n        }\n\n        /// <summary>\n        /// Manually run the task\n        /// </summary>\n        public void RunNow()\n        {\n            SafeRun();\n        }\n\n        /// <summary>\n        /// This method must be implemented by the child class and must contain the code\n        /// to be executed periodically.\n        /// </summary>\n        protected abstract void Run();\n\n        /// <summary>\n        /// Wrap Run method in Try/Catch\n        /// </summary>\n        private void SafeRun()\n        {\n            try\n            {\n                Run();\n            }\n            catch (Exception ex)\n            {\n                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, false));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Tasks/LowResolutionTimedTask.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Threading.Tasks;\nusing System.Timers;\n\nnamespace IntelliTrader.Core\n{\n    public abstract class LowResolutionTimedTask : ITimedTask\n    {\n        /// <summary>\n        /// Raised on unhandled exception\n        /// </summary>\n        #pragma warning disable CS0067 \n        public event UnhandledExceptionEventHandler UnhandledException;\n\n        /// <summary>\n        /// Delay before starting the task in milliseconds\n        /// </summary>\n        public double StartDelay { get; set; }\n\n        /// <summary>\n        /// Periodic execution interval in milliseconds\n        /// </summary>\n        public double Interval\n        {\n            get { return timer.Interval; }\n            set { timer.Interval = value; }\n        }\n\n        /// <summary>\n        /// How often to skip task execution (in RunCount)\n        /// </summary>\n        public int SkipIteration { get; set; } = 0;\n\n        /// <summary>\n        /// Stopwatch to use for timing the intervals\n        /// </summary>\n        public Stopwatch Stopwatch { get; set; }\n\n        /// <summary>\n        /// Indicates whether the task is currently running\n        /// </summary>\n        public bool IsRunning { get; private set; }\n\n        /// <summary>\n        /// Number of times the task has been run\n        /// </summary>\n        public long RunCount { get; private set; }\n\n        /// <summary>\n        /// Total time it took to run the task in milliseconds\n        /// </summary>\n        public double TotalRunTime { get; private set; }\n\n        /// <summary>\n        /// Total task run delay in milliseconds\n        /// </summary>\n        public double TotalLagTime { get; private set; }\n\n        private readonly Timer timer = new Timer();\n        private readonly Stopwatch runWatch = new Stopwatch();\n        private readonly System.Threading.AutoResetEvent syncMutex = new System.Threading.AutoResetEvent(true);\n\n        /// <summary>\n        /// Class constructor. It allocates the memory for the background timer and\n        /// initializes sync mutex.\n        /// </summary>\n        public LowResolutionTimedTask()\n        {\n            this.timer.Elapsed += OnTimerElapsed;\n        }\n\n        /// <summary>\n        /// Starts the background task timer that is in charge of executing the Execute method each\n        /// time the interval is elapsed.\n        /// </summary>\n        public void Start()\n        {\n            if (!IsRunning)\n            {\n                IsRunning = true;\n\n                if (StartDelay > 0)\n                {\n                    Task.Delay((int)StartDelay).ContinueWith(t =>\n                    {\n                        if (IsRunning)\n                        {\n                            timer.Start();\n                        }\n                    });\n                }\n                else\n                {\n                    timer.Start();\n                }\n            }\n        }\n\n        /// <summary>\n        /// Stops the background task timer that is in charge of executing the Execute method each\n        /// time the interval is elapsed. If the Execute method was executing when this method is\n        /// called, the caller thread will block waiting the Execute operation to finish. Later on,\n        /// the timer will be stopped. Otherwise, if the Execute method is not executing when this\n        /// method is called, the timer will be stopped without blocking the caller thread.\n        /// </summary>\n        public void Stop()\n        {\n            Stop(-1);\n        }\n\n        /// <summary>\n        /// Stops the background task timer that is in charge of executing the Execute method each\n        /// time the interval is elapsed. If the Execute method was executing when this method is\n        /// called, the caller thread will block waiting the Execute operation to finish. Later on,\n        /// the timer will be stopped. Otherwise, if the Execute method is not executing when this\n        /// method is called, the timer will be stopped without blocking the caller thread.\n        /// </summary>\n        /// <param name=\"timeout\">Timeout value in milliseconds to wait before killing the task</param>\n        public void Stop(int timeout)\n        {\n            if (IsRunning)\n            {\n                this.syncMutex.WaitOne(timeout);\n                this.syncMutex.Set();\n                this.timer.Stop();\n                IsRunning = false;\n            }\n        }\n\n        /// <summary>\n        /// Stops the periodic task executor without waiting the current task to stop.\n        /// </summary>\n        public void Terminate()\n        {\n            if (IsRunning)\n            {\n                this.timer.Stop();\n                IsRunning = false;\n            }\n        }\n\n        /// <summary>\n        /// This method can operate in two different ways. If the Execute method is currently executing, it will\n        /// block the caller thread until Execute finishes. However, if the Execute method is not being executed,\n        /// this method will not block and will immediately return back the control to the caller thread.\n        /// </summary>\n        public void Join()\n        {\n            if (IsRunning)\n            {\n                this.syncMutex.WaitOne();\n                this.syncMutex.Set();\n            }\n        }\n\n        /// <summary>\n        /// Temporarily pause the task\n        /// </summary>\n        public void Pause()\n        {\n            this.timer.Enabled = false;\n        }\n\n        /// <summary>\n        ///  Continue running the task\n        /// </summary>\n        public void Continue()\n        {\n            this.timer.Enabled = true;\n        }\n\n        /// <summary>\n        /// Wraps the Execute call abstracting the child class from the thread synchronization issues.\n        /// </summary>\n        /// <param name=\"sender\">The thimer object that is calling the event listener.</param>\n        /// <param name=\"e\">The arguments passed by the timer to the method.</param>\n        private void OnTimerElapsed(object sender, ElapsedEventArgs e)\n        {\n            //Force other threads to wait until it's finished when calling join.\n            this.syncMutex.Reset();\n\n            //Avoid re-calling the method while it is still operating.\n            this.timer.Stop();\n\n            if (IsRunning)\n            {\n                runWatch.Restart();\n                this.Run();\n                long runTime = runWatch.ElapsedMilliseconds;\n                TotalLagTime += (runTime > Interval) ? (runTime - Interval) : 0;\n                TotalRunTime += runTime;\n                RunCount++;\n                runWatch.Stop();\n\n                //Re-Start the timer to execute the worker function endlessly.\n                this.timer.Start();\n            }\n\n            //Release threads that might be frozen in join operation.\n            this.syncMutex.Set();\n        }\n\n        /// <summary>\n        /// Manually run the task\n        /// </summary>\n        public void RunNow()\n        {\n            Run();\n        }\n\n        /// <summary>\n        /// This method must be implemented by the child class and must contain the code\n        /// to be executed periodically.\n        /// </summary>\n        protected abstract void Run();\n    }\n}\n\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/Arbitrage.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class Arbitrage\n    {\n        public bool IsAssigned { get; set; }\n        public ArbitrageMarket Market { get; set; }\n        public ArbitrageType Type { get; set; }\n        public decimal Percentage { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/ArbitrageMarket.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum ArbitrageMarket\n    {\n        ETH,\n        BNB,\n        USDT\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/ArbitrageOptions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class ArbitrageOptions\n    {\n        public string Pair { get; set; }\n        public Arbitrage Arbitrage { get; set; }\n        public bool ManualOrder { get; set; }\n        public OrderMetadata Metadata { get; set; }\n\n        public ArbitrageOptions(string pair, Arbitrage arbitrage, OrderMetadata newPairMetadata)\n        {\n            this.Pair = pair;\n            this.Arbitrage = arbitrage;\n            this.Metadata = newPairMetadata ?? new OrderMetadata();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/ArbitrageType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum ArbitrageType\n    {\n        Direct,\n        Reverse\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/BuyOptions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class BuyOptions\n    {\n        public string Pair { get; set; }\n        public decimal? Amount { get; set; }\n        public decimal? MaxCost { get; set; }\n        public decimal? Price { get; set; }\n        public bool ManualOrder { get; set; }\n        public bool Swap { get; set; }\n        public bool Arbitrage { get; set; }\n        public bool IgnoreExisting { get; set; }\n        public bool IgnoreBalance { get; set; }\n        public OrderMetadata Metadata { get; set; }\n\n        public BuyOptions(string pair)\n        {\n            this.Pair = pair;\n            this.Metadata = new OrderMetadata();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/BuyTrailingStopAction.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum BuyTrailingStopAction\n    {\n        Buy,\n        Cancel\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/DCALevel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class DCALevel\n    {\n        public decimal Margin { get; set; }\n        public decimal? BuyMultiplier { get; set; }\n        public double? BuySamePairTimeout { get; set; }\n        public decimal? BuyTrailing { get; set; }\n        public decimal? BuyTrailingStopMargin { get; set; }\n        public BuyTrailingStopAction? BuyTrailingStopAction { get; set; }\n        public decimal? SellMargin { get; set; }\n        public decimal? SellTrailing { get; set; }\n        public decimal? SellTrailingStopMargin { get; set; }\n        public SellTrailingStopAction? SellTrailingStopAction { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/OrderMetadata.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class OrderMetadata\n    {\n        public bool? IsTransitional { get; set; }\n        public string OriginalPair { get; set; }\n        public List<string> TradingRules { get; set; }\n        public string SignalRule { get; set; }\n        public List<string> Signals { get; set; }\n        public double? BoughtRating { get; set; }\n        public double? CurrentRating { get; set; }\n        public double? BoughtGlobalRating { get; set; }\n        public double? CurrentGlobalRating { get; set; }\n        public decimal? LastBuyMargin { get; set; }\n        public int? AdditionalDCALevels { get; set; }\n        public decimal? AdditionalCosts { get; set; }\n        public decimal? FeesNonDeductible { get; set; }\n        public string SwapPair { get; set; }\n        public string Arbitrage { get; set; }\n        public decimal? ArbitragePercentage { get; set; }\n\n        public OrderMetadata MergeWith(OrderMetadata metadata)\n        {\n            return new OrderMetadata\n            {\n                IsTransitional = metadata.IsTransitional ?? IsTransitional,\n                OriginalPair = metadata.OriginalPair ?? OriginalPair,\n                TradingRules = metadata.TradingRules ?? TradingRules,\n                SignalRule = metadata.SignalRule ?? SignalRule,\n                Signals = metadata.Signals ?? Signals,\n                BoughtRating = metadata.BoughtRating ?? BoughtRating,\n                CurrentRating = metadata.CurrentRating ?? CurrentRating,\n                BoughtGlobalRating = metadata.BoughtGlobalRating ?? BoughtGlobalRating,\n                CurrentGlobalRating = metadata.CurrentGlobalRating ?? CurrentGlobalRating,\n                LastBuyMargin = metadata.LastBuyMargin ?? LastBuyMargin,\n                AdditionalDCALevels = metadata.AdditionalDCALevels ?? AdditionalDCALevels,\n                AdditionalCosts = metadata.AdditionalCosts ?? AdditionalCosts,\n                FeesNonDeductible = metadata.FeesNonDeductible ?? FeesNonDeductible,\n                SwapPair = metadata.SwapPair ?? SwapPair,\n                Arbitrage = metadata.Arbitrage ?? Arbitrage,\n                ArbitragePercentage = metadata.ArbitragePercentage ?? ArbitragePercentage\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/OrderResult.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum OrderResult\n    {\n        Unknown = 0,\n        Filled = 1,\n        FilledPartially = 2,\n        Pending = 3,\n        Error = 4,\n        Canceled = 5\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/OrderSide.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum OrderSide\n    {\n        Buy,\n        Sell\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/OrderType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum OrderType\n    {\n        Limit,\n        Market\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/RuleAction.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum RuleAction\n    {\n        Default,\n        Swap,\n        Arbitrage\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/SellOptions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class SellOptions\n    {\n        public string Pair { get; set; }\n        public decimal? Amount { get; set; }\n        public decimal? Price { get; set; }\n        public bool ManualOrder { get; set; }\n        public bool Swap { get; set; }\n        public bool Arbitrage { get; set; }\n        public OrderMetadata Metadata { get; set; }\n\n        public SellOptions(string pair)\n        {\n            this.Pair = pair;\n            this.Metadata = new OrderMetadata();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/SellTrailingStopAction.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public enum SellTrailingStopAction\n    {\n        Sell,\n        Cancel\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/SwapOptions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public class SwapOptions\n    {\n        public string OldPair { get; set; }\n        public string NewPair { get; set; }\n        public bool ManualOrder { get; set; }\n        public OrderMetadata Metadata { get; set; }\n\n        public SwapOptions(string oldPair, string newPair, OrderMetadata newPairMetadata)\n        {\n            this.OldPair = oldPair;\n            this.NewPair = newPair;\n            this.Metadata = newPairMetadata ?? new OrderMetadata();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/TradePriceType.cs",
    "content": "﻿namespace IntelliTrader.Core\n{\n    public enum TradePriceType\n    {\n        Last,\n        Ask,\n        Bid\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Trading/TradeResult.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader.Core\n{\n    public class TradeResult : ITradeResult\n    {\n        public bool IsSuccessful { get; set; }\n        public bool IsSwap { get; set; }\n        public bool IsArbitrage { get; set; }\n        public string Pair { get; set; }\n        public decimal Amount { get; set; }\n        public List<DateTimeOffset> OrderDates { get; set; }\n        public decimal AveragePrice { get; set; }\n        public decimal Fees { get; set; }\n        public decimal FeesTotal => Fees + (Metadata?.FeesNonDeductible ?? 0);\n        public decimal Cost => AveragePrice * Amount;\n        public DateTimeOffset SellDate { get; set; }\n        public decimal SellPrice { get; set; }\n        public decimal SellCost => SellPrice * Amount;\n        public decimal BalanceOffset { get; set; }\n        public decimal Profit { get; set; }\n        public OrderMetadata Metadata { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Models/Utils.cs",
    "content": "﻿namespace IntelliTrader.Core\n{\n    public static class Utils\n    {\n        public static decimal CalculatePercentage(decimal oldValue, decimal newValue)\n        {\n            if (oldValue != 0)\n            {\n                return (newValue - oldValue) / oldValue * 100;\n            }\n            else\n            {\n                return 0;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Serialization/DecimalFormatJsonConverter.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\n\nnamespace IntelliTrader.Core\n{\n    public class DecimalFormatJsonConverter : JsonConverter\n    {\n        private readonly int _numberOfDecimals;\n\n        public DecimalFormatJsonConverter(int numberOfDecimals)\n        {\n            _numberOfDecimals = numberOfDecimals;\n        }\n\n        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n        {\n            var d = (decimal)value;\n            var rounded = Math.Round(d, _numberOfDecimals, MidpointRounding.AwayFromZero);\n            writer.WriteValue(rounded);\n        }\n\n        public override object ReadJson(JsonReader reader, Type objectType, object existingValue,\n            JsonSerializer serializer)\n        {\n            throw new NotImplementedException(\"Unnecessary because CanRead is false. The type will skip the converter.\");\n        }\n\n        public override bool CanRead\n        {\n            get { return false; }\n        }\n\n        public override bool CanConvert(Type objectType)\n        {\n            return objectType == typeof(decimal);\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Services/ConfigurableServiceBase.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.Primitives;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    public abstract class ConfigrableServiceBase<TConfig> : IConfigurableService\n        where TConfig : class\n    {\n        private const double DELAY_BETWEEN_CONFIG_RELOADS_MILLISECONDS = 500;\n\n        public abstract string ServiceName { get; }\n\n        public TConfig Config\n        {\n            get\n            {\n                lock (syncRoot)\n                {\n                    if (config == null)\n                    {\n                        config = RawConfig.Get<TConfig>();\n                        PrepareConfig();\n                    }\n                    return config;\n                }\n            }\n        }\n\n        public IConfigurationSection RawConfig\n        {\n            get\n            {\n                lock (syncRoot)\n                {\n                    if (rawConfig == null)\n                    {\n                        rawConfig = Application.ConfigProvider.GetSection(ServiceName, OnRawConfigChanged);\n                    }\n                    return rawConfig;\n                }\n            }\n        }\n\n        private TConfig config;\n        private IConfigurationSection rawConfig;\n        private DateTimeOffset lastReloadDate;\n        private object syncRoot = new object();\n\n        protected virtual void PrepareConfig() { }\n        protected virtual void OnConfigReloaded() { }\n\n        private void OnRawConfigChanged(IConfigurationSection changedRawConfig)\n        {\n            lock (syncRoot)\n            {\n                rawConfig = changedRawConfig;\n                config = null;\n            }\n\n            if ((DateTimeOffset.Now - lastReloadDate).TotalMilliseconds > DELAY_BETWEEN_CONFIG_RELOADS_MILLISECONDS)\n            {\n                lastReloadDate = DateTimeOffset.Now;\n                PrepareConfig();\n                OnConfigReloaded();\n                Application.Resolve<ILoggingService>().Info($\"{ServiceName} configuration reloaded\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Services/CoreService.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing System.Globalization;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Core\n{\n    internal class CoreService : ConfigrableServiceBase<CoreConfig>, ICoreService\n    {\n        public event Action Started;\n\n        public override string ServiceName => Constants.ServiceNames.CoreService;\n\n        ICoreConfig ICoreService.Config => Config;\n\n        public string Version { get; private set; }\n        public string VersionType { get; private set; } = \" Beta\";\n\n        private readonly ILoggingService loggingService;\n        private readonly ITasksService tasksService;\n        private readonly INotificationService notificationService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITradingService tradingService;\n        private readonly IWebService webService;\n        private readonly IBacktestingService backtestingService;\n\n        public CoreService(ILoggingService loggingService, ITasksService tasksService, INotificationService notificationService, IHealthCheckService healthCheckService, ITradingService tradingService, IWebService webService, IBacktestingService backtestingService)\n        {\n            this.loggingService = loggingService;\n            this.tasksService = tasksService;\n            this.notificationService = notificationService;\n            this.healthCheckService = healthCheckService;\n            this.tradingService = tradingService;\n            this.webService = webService;\n            this.backtestingService = backtestingService;\n\n            // Log unhandled exceptions\n            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;\n            tasksService.SetUnhandledExceptionHandler(OnUnhandledException);\n\n            // Set decimal separator to a dot for all cultures\n            var cultureInfo = new CultureInfo(CultureInfo.CurrentCulture.Name);\n            cultureInfo.NumberFormat.NumberDecimalSeparator = \".\";\n            CultureInfo.DefaultThreadCurrentCulture = cultureInfo;\n            CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;\n\n            Version = GetType().Assembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version + VersionType;\n        }\n\n        public void Start()\n        {\n            loggingService.Info($\"Start Core service (Version: {Version})...\");\n\n            if (backtestingService.Config.Enabled)\n            {\n                backtestingService.Start();\n            }\n            if (Config.HealthCheckInterval > 0 && (!backtestingService.Config.Enabled || !backtestingService.Config.Replay))\n            {\n                healthCheckService.Start();\n            }\n            if (tradingService.Config.Enabled)\n            {\n                tradingService.Start();\n            }\n            if (notificationService.Config.Enabled)\n            {\n                notificationService.Start();\n            }\n            if (webService.Config.Enabled)\n            {\n                webService.Start();\n            }\n\n            ThreadPool.QueueUserWorkItem((state) =>\n            {\n                Thread.Sleep(2000);\n                Started?.Invoke();\n                tasksService.StartAllTasks();\n            });\n\n            loggingService.Info(\"Core service started\");\n            notificationService.Notify($\"IntelliTrader started\");\n        }\n\n        public void Stop()\n        {\n            notificationService.Notify(\"IntelliTrader stopped\");\n            loggingService.Info(\"Stop Core service...\");\n            if (tradingService.Config.Enabled)\n            {\n                tradingService.Stop();\n            }\n            if (notificationService.Config.Enabled)\n            {\n                notificationService.Stop();\n            }\n            if (webService.Config.Enabled)\n            {\n                webService.Stop();\n            }\n            if (Config.HealthCheckInterval > 0 && (!backtestingService.Config.Enabled || !backtestingService.Config.Replay))\n            {\n                healthCheckService.Stop();\n            }\n            if (backtestingService.Config.Enabled)\n            {\n                backtestingService.Stop();\n            }\n\n            tasksService.StopAllTasks();\n            tasksService.RemoveAllTasks();\n            loggingService.Info(\"Core service stopped\");\n        }\n\n        public void Restart()\n        {\n            notificationService.Notify(\"IntelliTrader restarting...\");\n            loggingService.Info(\"Restart Core service...\");\n            Task.Run(() => Stop()).Wait(TimeSpan.FromSeconds(20));\n            Start();\n        }\n\n        private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)\n        {\n            string message = \"Unhandled exception occured\";\n            if (e.ExceptionObject != null)\n            {\n                message = $\"{message} - {e.ExceptionObject}\";\n            }\n            try\n            {\n                loggingService.Error(message);\n                notificationService.Notify(message);\n            } catch { }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Services/HealthCheckService.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader.Core\n{\n    internal class HealthCheckService : IHealthCheckService\n    {\n        private readonly ILoggingService loggingService;\n        private readonly INotificationService notificationService;\n        private readonly ITasksService tasksService;\n\n        private readonly ConcurrentDictionary<string, HealthCheck> healthChecks = new ConcurrentDictionary<string, HealthCheck>();\n        private HealthCheckTimedTask healthCheckTimedTask;\n\n        public HealthCheckService(ILoggingService loggingService, INotificationService notificationService, ITasksService tasksService)\n        {\n            this.loggingService = loggingService;\n            this.notificationService = notificationService;\n            this.tasksService = tasksService;\n        }\n\n        public void Start()\n        {\n            loggingService.Info($\"Start Health Check service...\");\n\n            healthCheckTimedTask = tasksService.AddTask(\n                name: nameof(HealthCheckTimedTask),\n                task: new HealthCheckTimedTask(loggingService, notificationService, this, Application.Resolve<ICoreService>(), Application.Resolve<ITradingService>()),\n                interval: Application.Resolve<ICoreService>().Config.HealthCheckInterval * 1000 / Application.Speed,\n                startDelay: Constants.TaskDelays.HighDelay,\n                startTask: false,\n                runNow: false,\n                skipIteration: 0);\n\n            loggingService.Info(\"Health Check service started\");\n        }\n\n        public void Stop()\n        {\n            loggingService.Info($\"Stop Health Check service...\");\n\n            tasksService.RemoveTask(nameof(HealthCheckTimedTask), stopTask: true);\n\n            loggingService.Info(\"Health Check service stopped\");\n        }\n\n        public void UpdateHealthCheck(string name, string message = null, bool failed = false)\n        {\n            if (!healthChecks.TryGetValue(name, out HealthCheck existingHealthCheck))\n            {\n                healthChecks.TryAdd(name, new HealthCheck\n                {\n                    Name = name,\n                    Message = message,\n                    LastUpdated = DateTimeOffset.Now,\n                    Failed = failed\n                });\n            }\n            else\n            {\n                healthChecks[name].Message = message;\n                healthChecks[name].LastUpdated = DateTimeOffset.Now;\n                healthChecks[name].Failed = failed;\n            }\n        }\n\n        public void RemoveHealthCheck(string name)\n        {\n            healthChecks.TryRemove(name, out HealthCheck healthCheck);\n        }\n\n        public IEnumerable<IHealthCheck> GetHealthChecks()\n        {\n            foreach (var kvp in healthChecks)\n            {\n                yield return kvp.Value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Services/LoggingService.cs",
    "content": "﻿using Microsoft.Extensions.Configuration;\nusing Serilog;\nusing Serilog.Core;\nusing Serilog.Events;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nnamespace IntelliTrader.Core\n{\n    internal class LoggingService : ConfigrableServiceBase<LoggingConfig>, ILoggingService\n    {\n        private int LOG_ENTRIES_MAX_LENGTH = 50000;\n\n        public override string ServiceName => Constants.ServiceNames.LoggingService;\n\n        private Logger logger;\n        private StringWriter writer;\n        private StringBuilder writerStringBuilder;\n        private string logsPath;\n\n        private readonly object syncRoot = new object();\n\n        public LoggingService()\n        {\n            if (Config.Enabled)\n            {\n                logger = CreateLogger();\n            }\n        }\n\n        public void Verbose(string message, Exception exception = null)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Verbose(exception, message);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Verbose(string message, params object[] propertyValues)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Verbose(message, propertyValues);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Debug(string message, Exception exception = null)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Debug(exception, message);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Debug(string message, params object[] propertyValues)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Debug(message, propertyValues);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Info(string message, Exception exception = null)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Information(exception, message);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Info(string message, params object[] propertyValues)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Information(message, propertyValues);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Warning(string message, Exception exception = null)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Warning(exception, message);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Warning(string message, params object[] propertyValues)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Warning(message, propertyValues);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Error(string message, Exception exception = null)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Error(exception, message);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Error(string message, params object[] propertyValues)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Error(message, propertyValues);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Fatal(string message, Exception exception = null)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Fatal(exception, message);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void Fatal(string message, params object[] propertyValues)\n        {\n            lock (syncRoot)\n            {\n                if (Config.Enabled)\n                {\n                    logger.Fatal(message, propertyValues);\n                    CleanUpOldLogEntries();\n                }\n            }\n        }\n\n        public void DeleteAllLogs()\n        {\n            lock(syncRoot)\n            {\n                logger.Dispose();\n                Directory.Delete(Path.Combine(Directory.GetCurrentDirectory(), logsPath), true);\n                logger = CreateLogger();\n            }\n        }\n\n        public string[] GetLogEntries()\n        {\n            lock (syncRoot)\n            {\n                if (writer != null)\n                {\n                    writer.Flush();\n                    return writer.GetStringBuilder().ToString().Split(new string[] { writer.NewLine }, StringSplitOptions.RemoveEmptyEntries);\n                }\n                else\n                {\n                    return new string[0];\n                }\n            }\n        }\n\n        protected override void OnConfigReloaded()\n        {\n            lock (syncRoot)\n            {\n                logger = CreateLogger();\n            }\n        }\n\n        private Logger CreateLogger()\n        {\n            lock (syncRoot)\n            {\n                string outputTemplate = GetConfigValue(\"outputTemplate\", RawConfig.GetChildren());\n                string filterExpression = GetConfigValue(\"expression\", RawConfig.GetChildren());\n                string pathFormat = GetConfigValue(\"pathFormat\", RawConfig.GetChildren());\n                logsPath = Path.GetDirectoryName(pathFormat);\n\n                writerStringBuilder = new StringBuilder();\n                writer = new StringWriter(writerStringBuilder);\n\n                return new LoggerConfiguration()\n                    .ReadFrom.ConfigurationSection(RawConfig)\n                    .WriteTo.Logger(config => config.WriteTo.Memory(writer, LogEventLevel.Information, outputTemplate).Filter.ByIncludingOnly(filterExpression))\n                    .CreateLogger();\n            }\n        }\n\n        private void CleanUpOldLogEntries()\n        {\n            lock (syncRoot)\n            {\n                if (writerStringBuilder.Length > LOG_ENTRIES_MAX_LENGTH)\n                {\n                    writerStringBuilder.Remove(0, writerStringBuilder.Length - LOG_ENTRIES_MAX_LENGTH);\n                }\n            }\n        }\n\n        private string GetConfigValue(string key, IEnumerable<IConfigurationSection> sections)\n        {\n            foreach (var section in sections)\n            {\n                if (section.Key == key)\n                {\n                    return section.Value;\n                }\n                else\n                {\n                    string value = GetConfigValue(key, section.GetChildren());\n                    if (value != null)\n                    {\n                        return value;\n                    }\n                }\n            }\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Services/NotificationService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing Telegram.Bot;\nusing Telegram.Bot.Types;\nusing Telegram.Bot.Types.Enums;\n\nnamespace IntelliTrader.Core\n{\n    internal class NotificationService : ConfigrableServiceBase<NotificationConfig>, INotificationService\n    {\n        public override string ServiceName => Constants.ServiceNames.NotificationService;\n\n        INotificationConfig INotificationService.Config => Config;\n\n        private readonly ILoggingService loggingService;\n\n        // Telegram\n        private TelegramBotClient telegramBotClient;\n        private ChatId telegramChatId;\n\n        public NotificationService(ILoggingService loggingService)\n        {\n            this.loggingService = loggingService;\n        }\n\n        public void Start()\n        {\n            try\n            {\n                loggingService.Info(\"Start Notification service...\");\n                if (Config.TelegramEnabled)\n                {\n                    telegramBotClient = new TelegramBotClient(Config.TelegramBotToken);\n                    var me = telegramBotClient.GetMeAsync().Result;\n                    telegramChatId = new ChatId(Config.TelegramChatId);\n                }\n                loggingService.Info(\"Notification service started\");\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error(\"Unable to start Notification service\", ex);\n                Config.Enabled = false;\n            }\n        }\n\n        public void Stop()\n        {\n            loggingService.Info(\"Stop Notification service...\");\n            if (Config.TelegramEnabled)\n            {\n                telegramBotClient = null;\n            }\n            loggingService.Info(\"Notification service stopped\");\n        }\n\n        public void Notify(string message)\n        {\n            if (Config.Enabled)\n            {\n                if (Config.TelegramEnabled)\n                {\n                    try\n                    {\n                        var instanceName = Application.Resolve<ICoreService>().Config.InstanceName;\n                        telegramBotClient.SendTextMessageAsync(telegramChatId, $\"({instanceName}) {message}\", ParseMode.Default, false, !Config.TelegramAlertsEnabled);\n                    }\n                    catch (Exception ex)\n                    {\n                        loggingService.Error(\"Unable to send Telegram message\", ex);\n                    }\n                }\n            }\n        }\n\n        protected override void OnConfigReloaded()\n        {\n            Stop();\n            Start();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/Services/TasksService.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\n\nnamespace IntelliTrader.Core\n{\n    internal class TasksService : ITasksService\n    {\n        private ConcurrentDictionary<string, ITimedTask> tasks = new ConcurrentDictionary<string, ITimedTask>();\n        private Stopwatch syncStopSwatch = new Stopwatch();\n        private UnhandledExceptionEventHandler exceptionHandler;\n\n        public T AddTask<T>(string name, T task, double interval, double startDelay = 0, bool startTask = true, bool runNow = false, int skipIteration = 0)\n            where T : ITimedTask\n        {\n            tasks[name] = task;\n            task.Interval = interval;\n            task.StartDelay = startDelay;\n            task.Stopwatch = syncStopSwatch;\n            task.SkipIteration = skipIteration;\n\n            if (exceptionHandler != null)\n            {\n                task.UnhandledException += exceptionHandler;\n            }\n            if (startTask)\n            {\n                task.Start();\n            }\n            if (runNow)\n            {\n                task.RunNow();\n            }\n            return task;\n        }\n\n        public void RemoveTask(string name, bool stopTask = true)\n        {\n            if (tasks.TryRemove(name, out ITimedTask task))\n            {\n                if (stopTask)\n                {\n                    task.Stop();\n                }\n                if (exceptionHandler != null)\n                {\n                    task.UnhandledException -= exceptionHandler;\n                }\n                task.Stopwatch = null;\n            }\n        }\n\n        public void StartAllTasks()\n        {\n            foreach (var task in tasks.Values)\n            {\n                task.Start();\n            }\n        }\n\n        public void StopAllTasks()\n        {\n            foreach (var task in tasks.Values)\n            {\n                task.Stop();\n            }\n            syncStopSwatch.Reset();\n        }\n\n        public void RemoveAllTasks()\n        {\n            foreach (var taskName in tasks.Keys)\n            {\n                RemoveTask(taskName);\n            }\n            syncStopSwatch.Reset();\n        }\n\n        public ITimedTask GetTask(string name)\n        {\n            if (tasks.TryGetValue(name, out ITimedTask task))\n            {\n                return task;\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        public T GetTask<T>(string name)\n        {\n            return (T)GetTask(name);\n        }\n\n        public IEnumerable<KeyValuePair<string, ITimedTask>> GetAllTasks()\n        {\n            return tasks.AsEnumerable();\n        }\n\n        public void SetUnhandledExceptionHandler(UnhandledExceptionEventHandler handler)\n        {\n            exceptionHandler = handler;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Core/TimedTasks/HealthCheckTimedTask.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Linq;\n\nnamespace IntelliTrader.Core\n{\n    internal class HealthCheckTimedTask : HighResolutionTimedTask\n    {\n        private readonly ILoggingService loggingService;\n        private readonly INotificationService notificationService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ICoreService coreService;\n        private readonly ITradingService tradingService;\n        private int healthCheckFailures = 0;\n\n        public HealthCheckTimedTask(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ICoreService coreService, ITradingService tradingService)\n        {\n            this.loggingService = loggingService;\n            this.notificationService = notificationService;\n            this.healthCheckService = healthCheckService;\n            this.coreService = coreService;\n            this.tradingService = tradingService;\n        }\n\n        protected override void Run()\n        {\n            if (coreService.Config.HealthCheckEnabled)\n            {\n                bool healthCheckFailed = false;\n                loggingService.Info(\"Health check results:\");\n\n                foreach (var healthCheck in healthCheckService.GetHealthChecks().OrderBy(c => c.Name))\n                {\n                    var elapsedSinceLastUpdate = (DateTimeOffset.Now - healthCheck.LastUpdated).TotalSeconds;\n                    bool healthCheckTimeout = coreService.Config.HealthCheckSuspendTradingTimeout > 0 && elapsedSinceLastUpdate > coreService.Config.HealthCheckSuspendTradingTimeout;\n                    string indicator = (healthCheck.Failed || healthCheckTimeout) ? \"[-]\" : \"[+]\";\n\n                    if (healthCheck.Message != null)\n                    {\n                        loggingService.Info($\" {indicator} ({healthCheck.LastUpdated:HH:mm:ss}) {healthCheck.Name} - {healthCheck.Message}\");\n                    }\n                    else\n                    {\n                        loggingService.Info($\" {indicator} ({healthCheck.LastUpdated:HH:mm:ss}) {healthCheck.Name}\");\n                    }\n\n                    if (healthCheck.Failed || healthCheckTimeout)\n                    {\n                        healthCheckFailed = true;\n                    }\n                }\n\n                if (healthCheckFailed)\n                {\n                    healthCheckFailures++;\n                }\n                else\n                {\n                    healthCheckFailures = 0;\n                }\n\n                if (healthCheckFailed && coreService.Config.HealthCheckFailuresToRestartServices > 0 && healthCheckFailures >= coreService.Config.HealthCheckFailuresToRestartServices)\n                {\n                    coreService.Restart();\n                }\n                else\n                {\n                    if (healthCheckFailed)\n                    {\n                        loggingService.Info($\"Health check failed ({healthCheckFailures})\");\n                        notificationService.Notify($\"Health check failed ({healthCheckFailures})\");\n\n                        if (!tradingService.IsTradingSuspended)\n                        {\n                            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingPairsProcessed);\n                            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingRulesProcessed);\n                            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.SignalRulesProcessed);\n                            tradingService.SuspendTrading();\n                        }\n                    }\n                    else if (!healthCheckFailed && tradingService.IsTradingSuspended)\n                    {\n                        loggingService.Info(\"Health check passed\");\n                        notificationService.Notify(\"Health check passed\");\n                        tradingService.ResumeTrading();\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/AppModule.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            \n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/IntelliTrader.Exchange.Base.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Compile Remove=\"Interfaces\\**\" />\n    <EmbeddedResource Remove=\"Interfaces\\**\" />\n    <None Remove=\"Interfaces\\**\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n    <ProjectReference Include=\"..\\Submodules\\ExchangeSharp\\ExchangeSharp\\ExchangeSharp.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/Models/BuyOrder.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing IntelliTrader.Core;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public class BuyOrder : Order\n    {\n        public override OrderSide Side => OrderSide.Buy;\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/Models/Config/ExchangeConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public class ExchangeConfig\n    {\n        public string KeysPath { get; set; }\n        public int RateLimitOccurences { get; set; }\n        public int RateLimitTimeframe { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/Models/Order.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public abstract class Order : IOrder\n    {\n        public abstract OrderSide Side { get; }\n        public OrderType Type { get; set; }\n        public DateTimeOffset Date { get; set; }\n        public string Pair { get; set; }\n        public decimal Amount { get; set; }\n        public decimal Price { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/Models/OrderDetails.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public class OrderDetails : IOrderDetails\n    {\n        public bool IsNormalized { get; set; }\n        public OrderSide Side { get; set; }\n        public OrderResult Result { get; set; }\n        public DateTimeOffset Date { get; set; }\n        public string OrderId { get; set; }\n        public string Pair { get; set; }\n        public string OriginalPair { get; set; }\n        public string Message { get; set; }\n        public decimal Amount { get; set; }\n        public decimal AmountFilled { get; set; }\n        public decimal Price { get; set; }\n        public decimal AveragePrice { get; set; }\n        public decimal Fees { get; set; }\n        public string FeesCurrency { get; set; }\n        public decimal Cost => AveragePrice * AmountFilled;\n        public OrderMetadata Metadata { get; set; } = new OrderMetadata();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/Models/SellOrder.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public class SellOrder : Order\n    {\n        public override OrderSide Side => OrderSide.Sell;\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/Models/Ticker.cs",
    "content": "﻿using IntelliTrader.Core;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public class Ticker : ITicker\n    {\n        public string Pair { get; set; }\n        public decimal BidPrice { get; set; }\n        public decimal AskPrice { get; set; }\n        public decimal LastPrice { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/Services/ExchangeService.cs",
    "content": "﻿using ExchangeSharp;\nusing IntelliTrader.Core;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Exchange.Base\n{\n    public abstract class ExchangeService : ConfigrableServiceBase<ExchangeConfig>, IExchangeService\n    {\n        public const int SOCKET_DISPOSE_TIMEOUT_MILLISECONDS = 10000;\n        public const int MAX_TICKERS_AGE_TO_RECONNECT_MILLISECONDS = 60000;\n        public const int INITIAL_TICKERS_TIMEOUT_MILLISECONDS = 5000;\n        public const int INITIAL_TICKERS_RETRY_LIMIT = 4;\n\n        public override string ServiceName => Constants.ServiceNames.ExchangeService;\n\n        protected readonly ILoggingService loggingService;\n        protected readonly IHealthCheckService healthCheckService;\n        protected readonly ITasksService tasksService;\n\n        public ExchangeAPI Api { get; set; }\n        public ConcurrentDictionary<string, Ticker> Tickers { get; private set; }\n\n        private IDisposable socket;\n        private ConcurrentBag<string> markets;\n        private TickersMonitorTimedTask tickersMonitorTimedTask;\n        private DateTimeOffset lastTickersUpdate;\n        private bool tickersStarted;\n\n        public ExchangeService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tasksService = tasksService;\n        }\n\n        public virtual void Start(bool virtualTrading)\n        {\n            loggingService.Info(\"Start Exchange service...\");\n            Api = InitializeApi();\n\n            if (!virtualTrading && !String.IsNullOrWhiteSpace(Config.KeysPath))\n            {\n                if (File.Exists(Config.KeysPath))\n                {\n                    loggingService.Info(\"Load keys from encrypted file...\");\n                    Api.LoadAPIKeys(Config.KeysPath);\n                }\n                else\n                {\n                    throw new FileNotFoundException(\"Keys file not found\");\n                }\n            }\n\n            loggingService.Info(\"Get initial ticker values...\");\n            IEnumerable<KeyValuePair<string, ExchangeTicker>> exchangeTickers = null;\n            for (int retry = 0; retry < INITIAL_TICKERS_RETRY_LIMIT; retry++)\n            {\n                Task.Run(() => exchangeTickers = Api.GetTickers()).Wait(TimeSpan.FromMilliseconds(INITIAL_TICKERS_TIMEOUT_MILLISECONDS));\n                if (exchangeTickers != null) break;\n            }\n            if (exchangeTickers != null)\n            {\n                Tickers = new ConcurrentDictionary<string, Ticker>(exchangeTickers.Select(t => new KeyValuePair<string, Ticker>(t.Key, new Ticker\n                {\n                    Pair = t.Key,\n                    AskPrice = t.Value.Ask,\n                    BidPrice = t.Value.Bid,\n                    LastPrice = t.Value.Last\n                })));\n                markets = new ConcurrentBag<string>(Tickers.Keys.Select(pair => GetPairMarket(pair)).Distinct().ToList());\n\n                lastTickersUpdate = DateTimeOffset.Now;\n                healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TickersUpdated, $\"Updates: {Tickers.Count}\");\n            }\n            else if (Tickers != null)\n            {\n                loggingService.Error(\"Unable to get initial ticker values\");\n            }\n            else\n            {\n                throw new Exception(\"Unable to get initial ticker values\");\n            }\n\n            ConnectTickersWebsocket();\n\n            loggingService.Info(\"Exchange service started\");\n        }\n\n        public virtual void Stop()\n        {\n            loggingService.Info(\"Stop Exchange service...\");\n\n            DisconnectTickersWebsocket();\n            lastTickersUpdate = DateTimeOffset.MinValue;\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TickersUpdated);\n\n            loggingService.Info(\"Exchange service stopped\");\n        }\n\n        protected abstract ExchangeAPI InitializeApi();\n\n        public abstract IOrderDetails PlaceOrder(IOrder order);\n\n        public virtual decimal ClampOrderAmount(string pair, decimal amount)\n        {\n            ExchangeMarket market = Api.GetExchangeMarketFromCache(pair);\n            return market == null ? amount : CryptoUtility.ClampDecimal(market.MinTradeSize, market.MaxTradeSize, market.QuantityStepSize, amount);\n        }\n\n        public virtual decimal ClampOrderPrice(string pair, decimal price)\n        {\n            ExchangeMarket market = Api.GetExchangeMarketFromCache(pair);\n            return market == null ? price : CryptoUtility.ClampDecimal(market.MinPrice, market.MaxPrice, market.PriceStepSize, price);\n        }\n\n        public void ConnectTickersWebsocket()\n        {\n            try\n            {\n                loggingService.Info(\"Connect to Exchange tickers...\");\n                socket = Api.GetTickersWebSocket(OnTickersUpdated);\n                loggingService.Info(\"Connected to Exchange tickers\");\n\n                tickersMonitorTimedTask = tasksService.AddTask(\n                    name: nameof(TickersMonitorTimedTask),\n                    task: new TickersMonitorTimedTask(loggingService, this),\n                    interval: MAX_TICKERS_AGE_TO_RECONNECT_MILLISECONDS / 2,\n                    startDelay: Constants.TaskDelays.ZeroDelay,\n                    startTask: tickersStarted,\n                    runNow: false,\n                    skipIteration: 0);\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error(\"Unable to connect to Exchange tickers\", ex);\n            }\n        }\n\n        public void DisconnectTickersWebsocket()\n        {\n            try\n            {\n                tasksService.RemoveTask(nameof(TickersMonitorTimedTask), stopTask: true);\n\n                loggingService.Info(\"Disconnect from Exchange tickers...\");\n                // Give Dispose 10 seconds to complete and then time out if not\n                Task.Run(() => socket.Dispose()).Wait(TimeSpan.FromMilliseconds(SOCKET_DISPOSE_TIMEOUT_MILLISECONDS));\n                socket = null;\n                loggingService.Info(\"Disconnected from Exchange tickers\");\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error(\"Unable to disconnect from Exchange tickers\", ex);\n            }\n        }\n\n        public virtual IEnumerable<ITicker> GetTickers()\n        {\n            return Tickers.Values;\n        }\n\n        public virtual IEnumerable<string> GetMarkets()\n        {\n            return markets.AsEnumerable();\n        }\n\n        public virtual IEnumerable<string> GetMarketPairs(string market)\n        {\n            return Tickers.Keys.Where(t => t.EndsWith(market));\n        }\n\n        public virtual Dictionary<string, decimal> GetAvailableAmounts()\n        {\n            return Api.GetAmountsAvailableToTradeAsync().Result;\n        }\n\n        public abstract IEnumerable<IOrderDetails> GetTrades(string pair);\n\n        public virtual decimal GetPrice(string pair, TradePriceType priceType)\n        {\n            if (Tickers.TryGetValue(pair, out Ticker ticker))\n            {\n                if (priceType == TradePriceType.Ask)\n                {\n                    return ticker.AskPrice;\n                }\n                else if (priceType == TradePriceType.Bid)\n                {\n                    return ticker.BidPrice;\n                }\n                else\n                {\n                    return ticker.LastPrice;\n                }\n            }\n            else\n            {\n                return 0;\n            }\n        }\n\n        public virtual decimal GetPriceSpread(string pair)\n        {\n            if (Tickers.TryGetValue(pair, out Ticker ticker))\n            {\n                return Utils.CalculatePercentage(ticker.BidPrice, ticker.AskPrice);\n            }\n            else\n            {\n                return 0;\n            }\n        }\n\n        public abstract Arbitrage GetArbitrage(string pair, string tradingMarket, List<ArbitrageMarket> arbitrageMarkets = null, ArbitrageType? arbitrageType = null);\n\n        public abstract string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket);\n\n        public virtual string GetPairMarket(string pair)\n        {\n            return Api.ExchangeSymbolToGlobalSymbol(pair).Split('-')[0];\n        }\n\n        public virtual string ChangeMarket(string pair, string market)\n        {\n            if (!pair.StartsWith(market) && !pair.EndsWith(market))\n            {\n                string currentMarket = GetPairMarket(pair);\n                return pair.Substring(0, pair.Length - currentMarket.Length) + market;\n            }\n            return pair;\n        }\n\n        public virtual decimal ConvertPrice(string pair, decimal price, string market, TradePriceType priceType)\n        {\n            string pairMarket = GetPairMarket(pair);\n            if (pairMarket == Constants.Markets.USDT)\n            {\n                string marketPair = market + pairMarket;\n                return price / GetPrice(marketPair, priceType);\n            }\n            else\n            {\n                string marketPair = pairMarket + market;\n                return GetPrice(marketPair, priceType) * price;\n            }\n        }\n\n        public TimeSpan GetTimeElapsedSinceLastTickersUpdate()\n        {\n            return DateTimeOffset.Now - lastTickersUpdate;\n        }\n\n        private void OnTickersUpdated(IReadOnlyCollection<KeyValuePair<string, ExchangeTicker>> updatedTickers)\n        {\n            if (!tickersStarted)\n            {\n                loggingService.Info(\"Ticker updates are working, good!\");\n                tickersStarted = true;\n            }\n\n            healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TickersUpdated, $\"Updates: {updatedTickers.Count}\");\n\n            lastTickersUpdate = DateTimeOffset.Now;\n\n            foreach (var update in updatedTickers)\n            {\n                if (Tickers.TryGetValue(update.Key, out Ticker ticker))\n                {\n                    ticker.AskPrice = update.Value.Ask;\n                    ticker.BidPrice = update.Value.Bid;\n                    ticker.LastPrice = update.Value.Last;\n                }\n                else\n                {\n                    Tickers.TryAdd(update.Key, new Ticker\n                    {\n                        Pair = update.Key,\n                        AskPrice = update.Value.Ask,\n                        BidPrice = update.Value.Bid,\n                        LastPrice = update.Value.Last\n                    });\n\n                    var market = GetPairMarket(update.Key);\n                    if (!markets.Contains(market))\n                    {\n                        markets.Add(market);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Base/TimedTasks/TickersMonitorTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Exchange.Base;\n\nnamespace IntelliTrader.Exchange\n{\n    internal class TickersMonitorTimedTask : HighResolutionTimedTask\n    {\n        private readonly ILoggingService loggingService;\n        private readonly IExchangeService exchangeService;\n\n        public TickersMonitorTimedTask(ILoggingService loggingService, IExchangeService exchangeService)\n        {\n            this.loggingService = loggingService;\n            this.exchangeService = exchangeService;\n        }\n\n        protected override void Run()\n        {\n            if (exchangeService.GetTimeElapsedSinceLastTickersUpdate().TotalMilliseconds > ExchangeService.MAX_TICKERS_AGE_TO_RECONNECT_MILLISECONDS)\n            {\n                loggingService.Info(\"Exchange max tickers age reached, reconnecting...\");\n                exchangeService.DisconnectTickersWebsocket();\n                exchangeService.ConnectTickersWebsocket();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Binance/AppModule.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Exchange.Binance\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<BinanceExchangeService>().Named<IExchangeService>(\"Binance\").As<IConfigurableService>().Named<IConfigurableService>(\"ExchangeBinance\").SingleInstance().PreserveExistingDefaults();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Binance/BinanceExchangeService.cs",
    "content": "﻿using ExchangeSharp;\nusing IntelliTrader.Core;\nusing IntelliTrader.Exchange.Base;\nusing System;\nusing System.Linq;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader.Exchange.Binance\n{\n    internal class BinanceExchangeService : ExchangeService\n    {\n        public BinanceExchangeService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService) :\n            base(loggingService, healthCheckService, tasksService)\n        {\n\n        }\n\n        protected override ExchangeAPI InitializeApi()\n        {\n            var binanceApi = new ExchangeBinanceAPI\n            {\n                RateLimit = new RateGate(Config.RateLimitOccurences, TimeSpan.FromSeconds(Config.RateLimitTimeframe))\n            };\n            return binanceApi;\n        }\n\n        public override IOrderDetails PlaceOrder(IOrder order)\n        {\n            var result = Api.PlaceOrderAsync(new ExchangeOrderRequest\n            {\n                OrderType = (ExchangeSharp.OrderType)(int)order.Type,\n                IsBuy = order.Side == OrderSide.Buy,\n                Amount = order.Amount,\n                Price = order.Price,\n                Symbol = order.Pair\n            }).Result;\n\n            return new OrderDetails\n            {\n                Side = result.IsBuy ? OrderSide.Buy : OrderSide.Sell,\n                Result = (OrderResult)(int)result.Result,\n                Date = result.OrderDate,\n                OrderId = result.OrderId,\n                Pair = result.Symbol,\n                Message = result.Message,\n                Amount = result.Amount,\n                AmountFilled = result.AmountFilled,\n                Price = result.Price,\n                AveragePrice = result.AveragePrice,\n                Fees = result.Fees,\n                FeesCurrency = result.FeesCurrency\n            };\n        }\n\n        public override IEnumerable<IOrderDetails> GetTrades(string pair)\n        {\n            var myTrades = new List<OrderDetails>();\n            var results = ((ExchangeBinanceAPI)Api).GetMyTrades(pair);\n\n            foreach (var result in results)\n            {\n                myTrades.Add(new OrderDetails\n                {\n                    Side = result.IsBuy ? OrderSide.Buy : OrderSide.Sell,\n                    Result = (OrderResult)(int)result.Result,\n                    Date = result.OrderDate,\n                    OrderId = result.OrderId,\n                    Pair = result.Symbol,\n                    Message = result.Message,\n                    Amount = result.Amount,\n                    AmountFilled = result.AmountFilled,\n                    Price = result.Price,\n                    AveragePrice = result.AveragePrice,\n                    Fees = result.Fees,\n                    FeesCurrency = result.FeesCurrency\n                });\n            }\n\n            return myTrades;\n        }\n\n        public override Arbitrage GetArbitrage(string pair, string tradingMarket, List<ArbitrageMarket> arbitrageMarkets = null, ArbitrageType? arbitrageType = null)\n        {\n            if (arbitrageMarkets == null || !arbitrageMarkets.Any())\n            {\n                arbitrageMarkets = new List<ArbitrageMarket> { ArbitrageMarket.ETH, ArbitrageMarket.BNB, ArbitrageMarket.USDT };\n            }\n            Arbitrage arbitrage = new Arbitrage\n            {\n                Market = arbitrageMarkets.First(),\n                Type = arbitrageType ?? ArbitrageType.Direct\n            };\n\n            try\n            {\n                if (tradingMarket == Constants.Markets.BTC)\n                {\n                    foreach (var market in arbitrageMarkets)\n                    {\n                        string marketPair = ChangeMarket(pair, market.ToString());\n                        string arbitragePair = GetArbitrageMarketPair(market);\n\n                        if (marketPair != pair &&\n                            Tickers.TryGetValue(pair, out Ticker pairTicker) &&\n                            Tickers.TryGetValue(marketPair, out Ticker marketTicker) &&\n                            Tickers.TryGetValue(arbitragePair, out Ticker arbitrageTicker))\n                        {\n                            decimal directArbitragePercentage = 0;\n                            decimal reverseArbitragePercentage = 0;\n\n                            if (market == ArbitrageMarket.ETH)\n                            {\n                                directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100;\n                                reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100;\n                            }\n                            else if (market == ArbitrageMarket.BNB)\n                            {\n                                directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100;\n                                reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100;\n                            }\n                            else if (market == ArbitrageMarket.USDT)\n                            {\n                                directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice / arbitrageTicker.AskPrice - 1) * 100;\n                                reverseArbitragePercentage = (arbitrageTicker.BidPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100;\n                            }\n\n                            if ((directArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Direct))\n                            {\n                                arbitrage.IsAssigned = true;\n                                arbitrage.Market = market;\n                                arbitrage.Type = ArbitrageType.Direct;\n                                arbitrage.Percentage = directArbitragePercentage;\n                            }\n\n                            if ((reverseArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Reverse))\n                            {\n                                arbitrage.IsAssigned = true;\n                                arbitrage.Market = market;\n                                arbitrage.Type = ArbitrageType.Reverse;\n                                arbitrage.Percentage = reverseArbitragePercentage;\n                            }\n                        }\n                    }\n                }\n            }\n            catch { }\n            return arbitrage;\n        }\n\n        public override string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket)\n        {\n            if (arbitrageMarket == ArbitrageMarket.ETH)\n            {\n                return Constants.Markets.ETH + Constants.Markets.BTC;\n            }\n            else if (arbitrageMarket == ArbitrageMarket.BNB)\n            {\n                return Constants.Markets.BNB + Constants.Markets.BTC;\n            }\n            else if (arbitrageMarket == ArbitrageMarket.USDT)\n            {\n                return Constants.Markets.BTC + Constants.Markets.USDT;\n            }\n            else\n            {\n                throw new NotSupportedException($\"Unsupported arbitrage market: {arbitrageMarket}\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Exchange.Binance/IntelliTrader.Exchange.Binance.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Compile Remove=\"Services\\**\" />\n    <EmbeddedResource Remove=\"Services\\**\" />\n    <None Remove=\"Services\\**\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Exchange.Base\\IntelliTrader.Exchange.Base.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Launcher/IntelliTrader.Launcher.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{B458FDA9-0E55-40BB-86C9-711F1FA18CDF}</ProjectGuid>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>IntelliTrader.Launcher</RootNamespace>\n    <AssemblyName>IntelliTrader</AssemblyName>\n    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile>Client</TargetFrameworkProfile>\n    <PublishUrl>publish\\</PublishUrl>\n    <Install>true</Install>\n    <InstallFrom>Disk</InstallFrom>\n    <UpdateEnabled>false</UpdateEnabled>\n    <UpdateMode>Foreground</UpdateMode>\n    <UpdateInterval>7</UpdateInterval>\n    <UpdateIntervalUnits>Days</UpdateIntervalUnits>\n    <UpdatePeriodically>false</UpdatePeriodically>\n    <UpdateRequired>false</UpdateRequired>\n    <MapFileExtensions>true</MapFileExtensions>\n    <ApplicationRevision>0</ApplicationRevision>\n    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>\n    <IsWebBootstrapper>false</IsWebBootstrapper>\n    <UseApplicationTrust>false</UseApplicationTrust>\n    <BootstrapperEnabled>true</BootstrapperEnabled>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>false</DebugSymbols>\n    <DebugType>none</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\IntelliTrader\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>none</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\IntelliTrader\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>IntelliTrader.ico</ApplicationIcon>\n  </PropertyGroup>\n  <PropertyGroup>\n    <StartupObject>IntelliTrader.Launcher.Program</StartupObject>\n  </PropertyGroup>\n  <PropertyGroup>\n    <NoWin32Manifest>true</NoWin32Manifest>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"IntelliTrader.ico\" />\n  </ItemGroup>\n  <ItemGroup>\n    <BootstrapperPackage Include=\"Microsoft.Net.Framework.3.5.SP1\">\n      <Visible>False</Visible>\n      <ProductName>.NET Framework 3.5 SP1</ProductName>\n      <Install>true</Install>\n    </BootstrapperPackage>\n  </ItemGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Windows.Forms\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "IntelliTrader.Launcher/Program.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing System.Windows.Forms;\n\nnamespace IntelliTrader.Launcher\n{\n    class Program\n    {\n        const int PROCESS_WAIT_TIMEOUT = 1;\n\n        [DllImport(\"user32.dll\")]\n        static extern int SetWindowText(IntPtr hWnd, string text);\n\n        static void Main(string[] args)\n        {\n            string instanceName = args.Length == 1 ? args[0] : null;\n            string processFileName = $\"{nameof(IntelliTrader)}.dll\";\n            string processPath = Directory.EnumerateFiles(Directory.GetCurrentDirectory(), processFileName, SearchOption.AllDirectories).FirstOrDefault();\n            if (!string.IsNullOrWhiteSpace(processPath))\n            {\n                Process process = new Process();\n                try\n                {\n                    process.StartInfo.FileName = \"dotnet\";\n                    process.StartInfo.Arguments = $\"\\\"{processPath}\\\"\";\n                    process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;\n                    process.Start();\n                    Thread.Sleep(TimeSpan.FromSeconds(PROCESS_WAIT_TIMEOUT));\n                    if (process.HasExited)\n                    {\n                        MessageBox.Show($\"Unable to start IntelliTrader.{Environment.NewLine}{Environment.NewLine}Please make sure you have the latest .NET Core Runtime installed from{Environment.NewLine}https://www.microsoft.com/net/download\", nameof(IntelliTrader));\n                        return;\n                    }\n                    SpinWait.SpinUntil(() =>\n                    {\n                        return process.MainWindowHandle != IntPtr.Zero;\n                    }, TimeSpan.FromSeconds(PROCESS_WAIT_TIMEOUT));\n                }\n                catch (Exception ex)\n                {\n                    MessageBox.Show($\"Error: {ex.Message}{Environment.NewLine}{Environment.NewLine}Please download the latest .NET Core Runtime from{Environment.NewLine}https://www.microsoft.com/net/download\", nameof(IntelliTrader));\n                }\n\n                try\n                {\n                    if (!String.IsNullOrWhiteSpace(instanceName))\n                    {\n                        SetWindowText(process.MainWindowHandle, $\"{nameof(IntelliTrader)} - {instanceName}\");\n                    }\n                    else\n                    {\n                        SetWindowText(process.MainWindowHandle, $\"{nameof(IntelliTrader)}\");\n                    }\n                }\n                catch { }\n            }\n            else\n            {\n                MessageBox.Show($\"{processFileName} not found\", nameof(IntelliTrader));\n            }\n        }\n    }\n}"
  },
  {
    "path": "IntelliTrader.Launcher/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"IntelliTrader.Launcher\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"IntelliTrader.Launcher\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2018\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"b458fda9-0e55-40bb-86c9-711f1fa18cdf\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "IntelliTrader.Rules/AppModule.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\nusing System;\n\nnamespace IntelliTrader.Rules\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<RulesService>().As<IRulesService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.RulesService).SingleInstance();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Rules/Config/RulesConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Rules\n{\n    internal class RulesConfig : IRulesConfig\n    {\n        public IEnumerable<ModuleRules> Modules { get; set; }\n        IEnumerable<IModuleRules> IRulesConfig.Modules => Modules;\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Rules/IntelliTrader.Rules.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Rules/Models/ModuleRules.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Rules\n{\n    internal class ModuleRules : IModuleRules\n    {\n        public string Module { get; set; }\n        public IConfigurationSection Configuration { get; set; }\n        public IEnumerable<Rule> Entries { get; set; }\n        IEnumerable<IRule> IModuleRules.Entries => Entries;\n\n        public T GetConfiguration<T>()\n        {\n            return Configuration.Get<T>();\n        }\n    }\n}"
  },
  {
    "path": "IntelliTrader.Rules/Models/Rule.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Rules\n{\n    internal class Rule : IRule\n    {\n        public bool Enabled { get; set; }\n        public string Name { get; set; }\n        public RuleAction Action { get; set; }\n        public IEnumerable<RuleCondition> Conditions { get; set; }\n        public RuleTrailing Trailing { get; set; }\n        public IConfigurationSection Modifiers { get; set; }\n\n        IEnumerable<IRuleCondition> IRule.Conditions => Conditions;\n        IRuleTrailing IRule.Trailing => Trailing;\n\n        private object typedModifiersCached;\n\n        public T GetModifiers<T>()\n        {\n            if (typedModifiersCached == null)\n            {\n                typedModifiersCached = Modifiers.Get<T>();\n            }\n            return (T)typedModifiersCached;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Rules/Models/RuleCondition.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader.Rules\n{\n    internal class RuleCondition : IRuleCondition\n    {\n        public string Signal { get; set; }\n        public decimal? MinPrice { get; set; }\n        public decimal? MaxPrice { get; set; }\n        public decimal? MinSpread { get; set; }\n        public decimal? MaxSpread { get; set; }\n        public long? MinVolume { get; set; }\n        public long? MaxVolume { get; set; }\n        public double? MinVolumeChange { get; set; }\n        public double? MaxVolumeChange { get; set; }\n        public decimal? MinPriceChange { get; set; }\n        public decimal? MaxPriceChange { get; set; }\n        public double? MinRating { get; set; }\n        public double? MaxRating { get; set; }\n        public double? MinRatingChange { get; set; }\n        public double? MaxRatingChange { get; set; }\n        public double? MinVolatility { get; set; }\n        public double? MaxVolatility { get; set; }\n        public double? MinGlobalRating { get; set; }\n        public double? MaxGlobalRating { get; set; }\n\n        public decimal? MinArbitrage { get; set; }\n        public decimal? MaxArbitrage { get; set; }\n        public ArbitrageMarket? ArbitrageMarket { get; set; }\n        public ArbitrageType? ArbitrageType { get; set; }\n\n        public List<string> Pairs { get; set; }\n        public List<string> NotPairs { get; set; }\n\n        // Trading pair specific conditions\n        public double? MinAge { get; set; }\n        public double? MaxAge { get; set; }\n        public double? MinLastBuyAge { get; set; }\n        public double? MaxLastBuyAge { get; set; }\n        public decimal? MinMargin { get; set; }\n        public decimal? MaxMargin { get; set; }\n        public decimal? MinMarginChange { get; set; }\n        public decimal? MaxMarginChange { get; set; }\n        public decimal? MinAmount { get; set; }\n        public decimal? MaxAmount { get; set; }\n        public decimal? MinCost { get; set; }\n        public decimal? MaxCost { get; set; }\n        public int? MinDCALevel { get; set; }\n        public int? MaxDCALevel { get; set; }\n        public List<string> SignalRules { get; set; }\n        public List<string> NotSignalRules { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Rules/Models/RuleTrailing.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Rules\n{\n    internal class RuleTrailing : IRuleTrailing\n    {\n        public bool Enabled { get; set; }\n        public int MinDuration { get; set; }\n        public int MaxDuration { get; set; }\n        public IEnumerable<RuleCondition> StartConditions { get; set; }\n\n        IEnumerable<IRuleCondition> IRuleTrailing.StartConditions => StartConditions;\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Rules/Services/RulesService.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace IntelliTrader.Rules\n{\n    internal class RulesService : ConfigrableServiceBase<RulesConfig>, IRulesService\n    {\n        public override string ServiceName => Constants.ServiceNames.RulesService;\n\n        IRulesConfig IRulesService.Config => Config;\n\n        private readonly ILoggingService loggingService;\n        private readonly ITradingService tradingService;\n        private readonly List<Action> rulesChangeCallbacks = new List<Action>();\n\n        public RulesService(ILoggingService loggingService, ITradingService tradingService)\n        {\n            this.loggingService = loggingService;\n            this.tradingService = tradingService;\n        }\n\n        public IModuleRules GetRules(string module)\n        {\n            IModuleRules moduleRules = Config.Modules.FirstOrDefault(m => m.Module == module);\n            if (moduleRules != null)\n            {\n                return moduleRules;\n            }\n            else\n            {\n                throw new Exception($\"Unable to find rules for {module}\");\n            }\n        }\n\n        public bool CheckConditions(IEnumerable<IRuleCondition> conditions, Dictionary<string, ISignal> signals, double? globalRating, string pair, ITradingPair tradingPair)\n        {\n            if (conditions != null)\n            {\n                foreach (var condition in conditions)\n                {\n                    ISignal signal = null;\n                    if (condition.Signal != null && signals.TryGetValue(condition.Signal, out ISignal s))\n                    {\n                        signal = s;\n                    }\n\n                    if (condition.MinPrice != null && (tradingService.GetPrice(pair) < condition.MinPrice) ||\n                        condition.MaxPrice != null && (tradingService.GetPrice(pair) > condition.MaxPrice) ||\n                        condition.MinSpread != null && (tradingService.Exchange.GetPriceSpread(pair) < condition.MinSpread) ||\n                        condition.MaxSpread != null && (tradingService.Exchange.GetPriceSpread(pair) > condition.MaxSpread) ||\n                        condition.MinArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, \n                        condition.ArbitrageMarket != null ? new List<ArbitrageMarket> { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage < condition.MinArbitrage ||\n                        condition.MaxArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, \n                        condition.ArbitrageMarket != null ? new List<ArbitrageMarket> { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage > condition.MaxArbitrage ||\n\n                        condition.MinVolume != null && (signal == null || signal.Volume == null || signal.Volume < condition.MinVolume) ||\n                        condition.MaxVolume != null && (signal == null || signal.Volume == null || signal.Volume > condition.MaxVolume) ||\n                        condition.MinVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange < condition.MinVolumeChange) ||\n                        condition.MaxVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange > condition.MaxVolumeChange) ||\n                        condition.MinPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange < condition.MinPriceChange) ||\n                        condition.MaxPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange > condition.MaxPriceChange) ||\n                        condition.MinRating != null && (signal == null || signal.Rating == null || signal.Rating < condition.MinRating) ||\n                        condition.MaxRating != null && (signal == null || signal.Rating == null || signal.Rating > condition.MaxRating) ||\n                        condition.MinRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange < condition.MinRatingChange) ||\n                        condition.MaxRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange > condition.MaxRatingChange) ||\n                        condition.MinVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility < condition.MinVolatility) ||\n                        condition.MaxVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility > condition.MaxVolatility) ||\n                        condition.MinGlobalRating != null && (globalRating == null || globalRating < condition.MinGlobalRating) ||\n                        condition.MaxGlobalRating != null && (globalRating == null || globalRating > condition.MaxGlobalRating) ||\n                        condition.Pairs != null && (pair == null || !condition.Pairs.Contains(pair)) ||\n                        condition.NotPairs != null && (pair == null || condition.NotPairs.Contains(pair)) ||\n\n                        condition.MinAge != null && (tradingPair == null || tradingPair.CurrentAge < condition.MinAge / Application.Speed) ||\n                        condition.MaxAge != null && (tradingPair == null || tradingPair.CurrentAge > condition.MaxAge / Application.Speed) ||\n                        condition.MinLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge < condition.MinLastBuyAge / Application.Speed) ||\n                        condition.MaxLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge > condition.MaxLastBuyAge / Application.Speed) ||\n                        condition.MinMargin != null && (tradingPair == null || tradingPair.CurrentMargin < condition.MinMargin) ||\n                        condition.MaxMargin != null && (tradingPair == null || tradingPair.CurrentMargin > condition.MaxMargin) ||\n                        condition.MinMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) < condition.MinMarginChange) ||\n                        condition.MaxMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) > condition.MaxMarginChange) ||\n                        condition.MinAmount != null && (tradingPair == null || tradingPair.Amount < condition.MinAmount) ||\n                        condition.MaxAmount != null && (tradingPair == null || tradingPair.Amount > condition.MaxAmount) ||\n                        condition.MinCost != null && (tradingPair == null || tradingPair.CurrentCost < condition.MinCost) ||\n                        condition.MaxCost != null && (tradingPair == null || tradingPair.CurrentCost > condition.MaxCost) ||\n                        condition.MinDCALevel != null && (tradingPair == null || tradingPair.DCALevel < condition.MinDCALevel) ||\n                        condition.MaxDCALevel != null && (tradingPair == null || tradingPair.DCALevel > condition.MaxDCALevel) ||\n                        condition.SignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || !condition.SignalRules.Contains(tradingPair.Metadata.SignalRule)) ||\n                        condition.NotSignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || condition.NotSignalRules.Contains(tradingPair.Metadata.SignalRule)))\n                    {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n\n        public void RegisterRulesChangeCallback(Action callback)\n        {\n            rulesChangeCallbacks.Add(callback);\n        }\n\n        public void UnregisterRulesChangeCallback(Action callback)\n        {\n            rulesChangeCallbacks.Remove(callback);\n        }\n\n        protected override void OnConfigReloaded()\n        {\n            foreach (var callback in rulesChangeCallbacks)\n            {\n                callback();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/AppModule.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Autofac;\nusing IntelliTrader.Core;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<SignalsService>().As<ISignalsService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.SignalsService).SingleInstance().PreserveExistingDefaults();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/IntelliTrader.Signals.Base.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Interfaces/ISignaReceiver.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public interface ISignalReceiver\n    {\n        string SignalName { get; }\n        void Start();\n        void Stop();\n        int GetPeriod();\n        IEnumerable<ISignal> GetSignals();\n        double? GetAverageRating();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Models/Config/SignalsConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public class SignalsConfig : ISignalsConfig\n    {\n        public bool Enabled { get; set; }\n        public IEnumerable<string> GlobalRatingSignals { get; set; }\n        public IEnumerable<SignalDefinition> Definitions { get; set; }\n\n        IEnumerable<ISignalDefinition> ISignalsConfig.Definitions => Definitions;\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Models/Signal.cs",
    "content": "﻿using IntelliTrader.Core;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public class Signal : ISignal\n    {\n        public string Name { get; set; }\n        public string Pair { get; set; }\n        public long? Volume { get; set; }\n        public double? VolumeChange { get; set; }\n        public decimal? Price { get; set; }\n        public decimal? PriceChange { get; set; }\n        public double? Rating { get; set; }\n        public double? RatingChange { get; set; }\n        public double? Volatility { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Models/SignalDefinition.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Microsoft.Extensions.Configuration;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public class SignalDefinition : ISignalDefinition\n    {\n        public string Name { get; set; }\n        public string Receiver { get; set; }\n        public IConfigurationSection Configuration { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Models/SignalRuleModifiers.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Signals.Base\n{\n    internal class SignalRuleModifiers\n    {\n        public decimal? CostMultiplier { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Models/SignalRulesConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public class SignalRulesConfig : ISignalRulesConfig\n    {\n        public RuleProcessingMode ProcessingMode { get; set; }\n        public double CheckInterval { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Models/SignalTrailingInfo.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Signals.Base\n{\n    internal class SignalTrailingInfo : ISignalTrailingInfo\n    {\n        public IRule Rule { get; set; }\n        public DateTimeOffset StartTime { get; set; }\n        public double Duration => (DateTimeOffset.Now - StartTime).TotalSeconds;\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/Services/SignalsService.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public class SignalsService : ConfigrableServiceBase<SignalsConfig>, ISignalsService\n    {\n        public override string ServiceName => Constants.ServiceNames.SignalsService;\n\n        ISignalsConfig ISignalsService.Config => Config;\n\n        public IModuleRules Rules { get; private set; }\n        public ISignalRulesConfig RulesConfig { get; private set; }\n\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITasksService tasksService;\n        private readonly ITradingService tradingService;\n        private readonly IRulesService rulesService;\n\n        private ConcurrentDictionary<string, ISignalReceiver> signalReceivers = new ConcurrentDictionary<string, ISignalReceiver>();\n        private SignalRulesTimedTask signalRulesTimedTask;\n\n        public SignalsService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService, ITradingService tradingService, IRulesService rulesService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tasksService = tasksService;\n            this.tradingService = tradingService;\n            this.rulesService = rulesService;\n        }\n\n        public void Start()\n        {\n            loggingService.Info(\"Start Signals service...\");\n\n            OnSignalRulesChanged();\n            rulesService.RegisterRulesChangeCallback(OnSignalRulesChanged);\n\n            signalReceivers.Clear();\n            foreach (var definition in Config.Definitions)\n            {\n                var receiver = Application.ResolveOptionalNamed<ISignalReceiver>(definition.Receiver,\n                    new TypedParameter(typeof(string), definition.Name),\n                    new TypedParameter(typeof(IConfigurationSection), definition.Configuration));\n\n                if (receiver != null)\n                {\n                    if (signalReceivers.TryAdd(definition.Name, receiver))\n                    {\n                        receiver.Start();\n                    }\n                    else\n                    {\n                        throw new Exception($\"Duplicate signal definition: {definition.Name}\");\n                    }\n                }\n                else\n                {\n                    throw new Exception($\"Signal receiver not found: {definition.Receiver}\");\n                }\n            }\n\n            signalRulesTimedTask = tasksService.AddTask(\n                name: nameof(SignalRulesTimedTask),\n                task: new SignalRulesTimedTask(loggingService, healthCheckService, tradingService, rulesService, this),\n                interval: RulesConfig.CheckInterval * 1000 / Application.Speed,\n                startDelay: Constants.TaskDelays.LowDelay,\n                startTask: false,\n                runNow: false,\n                skipIteration: 0);\n\n            loggingService.Info(\"Signals service started\");\n        }\n\n        public void Stop()\n        {\n            loggingService.Info(\"Stop Signals service...\");\n\n            foreach (var receiver in signalReceivers.Values)\n            {\n                receiver.Stop();\n            }\n            signalReceivers.Clear();\n\n            tasksService.RemoveTask(nameof(SignalRulesTimedTask), stopTask: true);\n\n            rulesService.UnregisterRulesChangeCallback(OnSignalRulesChanged);\n\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.SignalRulesProcessed);\n\n            loggingService.Info(\"Signals service stopped\");\n        }\n\n        public void ProcessPair(string pair, Dictionary<string, ISignal> signals)\n        {\n            IEnumerable<IRule> enabledRules = Rules.Entries.Where(r => r.Enabled);\n            foreach (IRule rule in enabledRules)\n            {\n                signalRulesTimedTask.ProcessRule(rule, signals, pair, signalRulesTimedTask.GetExcludedPairs(), GetGlobalRating());\n            }\n        }\n\n        public void StopTrailing()\n        {\n            signalRulesTimedTask.StopTrailing();\n        }\n\n        public List<string> GetTrailingSignals()\n        {\n            return signalRulesTimedTask.GetTrailingSignals();\n        }\n\n        public IEnumerable<ISignalTrailingInfo> GetTrailingInfo(string pair)\n        {\n            return signalRulesTimedTask.GetTrailingInfo(pair);\n        }\n\n        public IEnumerable<string> GetSignalNames()\n        {\n            return signalReceivers.OrderBy(r => r.Value.GetPeriod()).Select(r => r.Key);\n        }\n\n        public IEnumerable<ISignal> GetAllSignals()\n        {\n            return GetSignalsByName(null);\n        }\n\n        public IEnumerable<ISignal> GetSignalsByName(string signalName)\n        {\n            IEnumerable<ISignal> signals = null;\n            foreach (var kvp in signalReceivers.OrderBy(r => r.Value.GetPeriod()))\n            {\n                if (signalName == null || signalName == kvp.Key)\n                {\n                    ISignalReceiver receiver = kvp.Value;\n                    if (signals == null)\n                    {\n                        signals = receiver.GetSignals();\n                    }\n                    else\n                    {\n                        signals = signals.Concat(receiver.GetSignals());\n                    }\n                }\n            }\n            return signals;\n        }\n\n        public IEnumerable<ISignal> GetSignalsByPair(string pair)\n        {\n            foreach (var receiver in signalReceivers.Values.OrderBy(r => r.GetPeriod()))\n            {\n                var signal = receiver.GetSignals().FirstOrDefault(s => s.Pair == pair);\n                if (signal != null)\n                {\n                    yield return signal;\n                }\n            }\n        }\n\n        public ISignal GetSignal(string pair, string signalName)\n        {\n            return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair);\n        }\n\n        public double? GetRating(string pair, string signalName)\n        {\n            return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating;\n        }\n\n        public double? GetRating(string pair, IEnumerable<string> signalNames)\n        {\n            if (signalNames != null && signalNames.Count() > 0)\n            {\n                double ratingSum = 0;\n\n                foreach (var signalName in signalNames)\n                {\n                    var rating = GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating;\n                    if (rating != null)\n                    {\n                        ratingSum += rating.Value;\n                    }\n                    else\n                    {\n                        return null;\n                    }\n                }\n\n                return Math.Round(ratingSum / signalNames.Count(), 8);\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        public double? GetGlobalRating()\n        {\n            try\n            {\n                double ratingSum = 0;\n                double ratingCount = 0;\n\n                foreach (var kvp in signalReceivers)\n                {\n                    string signalName = kvp.Key;\n                    if (Config.GlobalRatingSignals.Contains(signalName))\n                    {\n                        ISignalReceiver receiver = kvp.Value;\n                        double? averageRating = receiver.GetAverageRating();\n                        if (averageRating != null)\n                        {\n                            ratingSum += averageRating.Value;\n                            ratingCount++;\n                        }\n                    }\n                }\n\n                if (ratingCount > 0)\n                {\n                    return Math.Round(ratingSum / ratingCount, 8);\n                }\n                else\n                {\n                    return null;\n                }\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error(\"Unable to get global rating\", ex);\n                return null;\n            }\n        }\n\n        private void OnSignalRulesChanged()\n        {\n            Rules = rulesService.GetRules(ServiceName);\n            RulesConfig = Rules.GetConfiguration<SignalRulesConfig>();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.Base/TimedTasks/SignalRulesTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace IntelliTrader.Signals.Base\n{\n    public class SignalRulesTimedTask : HighResolutionTimedTask\n    {\n        public bool LoggingEnabled { get; set; } = true;\n\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITradingService tradingService;\n        private readonly IRulesService rulesService;\n        private readonly ISignalsService signalsService;\n\n        private ConcurrentDictionary<string, List<SignalTrailingInfo>> trailingSignals = new ConcurrentDictionary<string, List<SignalTrailingInfo>>();\n\n        public SignalRulesTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService, IRulesService rulesService, ISignalsService signalsService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tradingService = tradingService;\n            this.rulesService = rulesService;\n            this.signalsService = signalsService;\n        }\n\n        protected override void Run()\n        {\n            ProcessTrailingSignals();\n            ProcessAllRules();\n        }\n\n        public void StopTrailing()\n        {\n            trailingSignals.Clear();\n        }\n\n        public void StopTrailing(string pair)\n        {\n            trailingSignals.TryRemove(pair, out List<SignalTrailingInfo> trailingInfo);\n        }\n\n        public List<string> GetTrailingSignals()\n        {\n            return trailingSignals.Keys.ToList();\n        }\n\n        public IEnumerable<ISignalTrailingInfo> GetTrailingInfo(string pair)\n        {\n            if (trailingSignals.TryGetValue(pair, out List<SignalTrailingInfo> trailingInfo))\n            {\n                return trailingInfo;\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        private void ProcessTrailingSignals()\n        {\n            double? globalRating = signalsService.GetGlobalRating();\n\n            foreach (var kvp in trailingSignals)\n            {\n                string pair = kvp.Key;\n                List<SignalTrailingInfo> trailingInfoList = kvp.Value;\n\n                for (int i = trailingInfoList.Count - 1; i >= 0; i--)\n                {\n                    SignalTrailingInfo trailingInfo = trailingInfoList[i];\n\n                    if (trailingInfo.Rule.Trailing.MaxDuration == 0 || trailingInfo.Duration <= trailingInfo.Rule.Trailing.MaxDuration / Application.Speed)\n                    {\n                        if (trailingInfo.Duration >= trailingInfo.Rule.Trailing.MinDuration / Application.Speed)\n                        {\n                            IEnumerable<ISignal> signalsByPair = signalsService.GetSignalsByPair(pair);\n                            if (signalsByPair != null)\n                            {\n                                Dictionary<string, ISignal> signals = signalsByPair.ToDictionary(s => s.Name, s => s);\n                                if (rulesService.CheckConditions(trailingInfo.Rule.Conditions, signals, globalRating, pair, null))\n                                {\n                                    IEnumerable<ISignal> ruleSignals = signals.Where(s => trailingInfo.Rule.Conditions.Any(c => c.Signal == s.Key)).Select(s => s.Value);\n                                    InitiateBuy(pair, trailingInfo.Rule, ruleSignals);\n                                }\n                            }\n                        }\n                    }\n                    else\n                    {\n                        trailingInfoList.RemoveAt(i);\n                        if (trailingInfoList.Count == 0)\n                        {\n                            StopTrailing(pair);\n                        }\n                        if (LoggingEnabled)\n                        {\n                            loggingService.Info($\"Cancel trailing signal for {pair}. Rule: {trailingInfo.Rule.Name}, Reason: max duration reached\");\n                        }\n                    }\n                }\n            }\n        }\n\n        public void ProcessAllRules()\n        {\n            if (tradingService.Config.BuyEnabled)\n            {\n                IEnumerable<ISignal> allSignals = signalsService.GetAllSignals();\n                if (allSignals != null)\n                {\n                    IEnumerable<IRule> enabledRules = signalsService.Rules.Entries.Where(r => r.Enabled);\n                    if (enabledRules.Any())\n                    {\n                        var groupedSignals = allSignals.Where(s => tradingService.GetPairConfig(s.Pair).BuyEnabled).GroupBy(s => s.Pair).ToDictionary(g => g.Key, g => g.ToDictionary(s => s.Name, s => s));\n                        double? globalRating = signalsService.GetGlobalRating();\n                        List<String> excludedPairs = GetExcludedPairs();\n\n                        if (signalsService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch)\n                        {\n                            excludedPairs.AddRange(trailingSignals.Keys);\n                        }\n\n                        foreach (IRule rule in enabledRules)\n                        {\n                            foreach (var group in groupedSignals)\n                            {\n                                Dictionary<string, ISignal> signals = group.Value;\n                                ProcessRule(rule, signals, group.Key, excludedPairs, globalRating);\n                            }\n                        }\n                    }\n\n                    healthCheckService.UpdateHealthCheck(Constants.HealthChecks.SignalRulesProcessed, $\"Rules: {enabledRules.Count()}, Trailing signals: {trailingSignals.Count}\");\n                }\n            }\n        }\n\n        public void ProcessRule(IRule rule, Dictionary<string, ISignal> signals, string pair, List<string> excludedPairs, double? globalRating)\n        {\n            IEnumerable<IRuleCondition> conditions = rule.Trailing != null && rule.Trailing.Enabled ? rule.Trailing.StartConditions : rule.Conditions;\n            ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair, includeDust: true);\n            List<SignalTrailingInfo> trailingInfoList;\n\n            if (!excludedPairs.Contains(pair) && (!trailingSignals.TryGetValue(pair, out trailingInfoList) || !trailingInfoList.Any(t => t.Rule == rule)) &&\n                (conditions == null || rulesService.CheckConditions(conditions, signals, globalRating, pair, tradingPair)))\n            {\n                IEnumerable<ISignal> ruleSignals = conditions != null ? signals.Where(s => conditions.Any(c => c.Signal == s.Key)).Select(s => s.Value) : new List<ISignal>();\n\n                if (rule.Trailing != null && rule.Trailing.Enabled)\n                {\n                    if (trailingInfoList == null)\n                    {\n                        trailingInfoList = new List<SignalTrailingInfo>();\n                        trailingSignals.TryAdd(pair, trailingInfoList);\n                    }\n\n                    trailingInfoList.Add(new SignalTrailingInfo\n                    {\n                        Rule = rule,\n                        StartTime = DateTimeOffset.Now\n                    });\n\n                    if (LoggingEnabled)\n                    {\n                        loggingService.Info($\"Start trailing signal for {pair}. Rule: {rule.Name}\");\n                    }\n                }\n                else\n                {\n                    InitiateBuy(pair, rule, ruleSignals);\n                }\n\n                if (signalsService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch)\n                {\n                    excludedPairs.Add(pair);\n                }\n            }\n        }\n\n        public List<string> GetExcludedPairs()\n        {\n            return tradingService.Config.ExcludedPairs\n                .Concat(tradingService.Account.GetTradingPairs().Select(p => p.Pair))\n                .Concat(tradingService.GetTrailingBuys()).ToList();\n        }\n\n        private void InitiateBuy(string pair, IRule rule, IEnumerable<ISignal> ruleSignals)\n        {\n            StopTrailing(pair);\n\n            IPairConfig pairConfig = tradingService.GetPairConfig(pair);\n            SignalRuleModifiers ruleModifiers = rule.GetModifiers<SignalRuleModifiers>();\n            if (LoggingEnabled)\n            {\n                loggingService.Info($\"Initiate buy request for {pair}. Rule: {rule.Name}\");\n            }\n\n            var buyOptions = new BuyOptions(pair)\n            {\n                MaxCost = pairConfig.BuyMaxCost * pairConfig.BuyMultiplier * (ruleModifiers?.CostMultiplier ?? 1),\n                Metadata = new OrderMetadata\n                {\n                    SignalRule = rule.Name,\n                    Signals = ruleSignals.Select(s => s.Name).ToList(),\n                    BoughtRating = ruleSignals.Any(s => s.Rating != null) ? ruleSignals.Where(s => s.Rating != null).Average(s => s.Rating) : null,\n                    BoughtGlobalRating = signalsService.GetGlobalRating()\n                }\n            };\n\n            tradingService.Buy(buyOptions);\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.TradingView/AppModule.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\nusing IntelliTrader.Signals.Base;\n\nnamespace IntelliTrader.Signals.TradingView\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<TradingViewCryptoSignalReceiver>().As<ISignalReceiver>().Named<ISignalReceiver>(nameof(TradingViewCryptoSignalReceiver));\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.TradingView/IntelliTrader.Signals.TradingView.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Signals.Base\\IntelliTrader.Signals.Base.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Signals.TradingView/Models/Config/TradingViewCryptoSignalReceiverConfig.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Signals.TradingView\n{\n    internal class TradingViewCryptoSignalReceiverConfig\n    {\n        public double PollingInterval { get; set; }\n        public int SignalPeriod { get; set; }\n        public string VolatilityPeriod { get; set; }\n        public string RequestUrl { get; set; }\n        public string RequestData { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.TradingView/Models/TradingViewCryptoSignalConverter.cs",
    "content": "﻿using IntelliTrader.Signals.Base;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing System;\nusing System.Linq;\n\nnamespace IntelliTrader.Signals.TradingView\n{\n    internal class TradingViewCryptoSignalConverter : JsonConverter\n    {\n        public override bool CanConvert(Type objectType)\n        {\n            return objectType == typeof(Signal);\n        }\n\n        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)\n        {\n            if (reader.TokenType == JsonToken.StartArray)\n            {\n                var array = JArray.Load(reader);\n                var item = (existingValue as Signal ?? new Signal());\n                item.Pair = (string)array.ElementAtOrDefault(0);\n                item.Price = (decimal?)array.ElementAtOrDefault(1);\n                item.PriceChange = (decimal?)array.ElementAtOrDefault(2);\n                item.Volume = (long?)array.ElementAtOrDefault(3);\n                item.Rating = (double?)array.ElementAtOrDefault(4);\n                item.Volatility = (double?)array.ElementAtOrDefault(5);\n                return item;\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n        {\n            throw new NotSupportedException();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.TradingView/Receivers/TradingViewCryptoSignalReceiver.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Signals.Base;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\n\nnamespace IntelliTrader.Signals.TradingView\n{\n    internal class TradingViewCryptoSignalReceiver : ISignalReceiver\n    {\n        public string SignalName { get; private set; }\n        public TradingViewCryptoSignalReceiverConfig Config { get; private set; }\n        \n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITasksService tasksService;\n        private readonly ISignalsService signalsService;\n        private readonly ITradingService tradingService;\n\n        private TradingViewCryptoSignalPollingTimedTask tradingViewCryptoSignalPollingTimedTask;\n\n        public TradingViewCryptoSignalReceiver(string signalName, \n            IConfigurationSection configuration,  ILoggingService loggingService, IHealthCheckService healthCheckService, \n            ITasksService tasksService, ISignalsService signalsService, ITradingService tradingService)\n        {\n            this.SignalName = signalName;\n            this.Config = configuration.Get<TradingViewCryptoSignalReceiverConfig>();\n\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tasksService = tasksService;\n            this.signalsService = signalsService;\n            this.tradingService = tradingService;\n        }\n\n        public void Start()\n        {\n            loggingService.Info(\"Start TradingViewCryptoSignalReceiver...\");\n\n            tradingViewCryptoSignalPollingTimedTask = tasksService.AddTask(\n                name: $\"{nameof(TradingViewCryptoSignalPollingTimedTask)} [{SignalName}]\",\n                task: new TradingViewCryptoSignalPollingTimedTask(loggingService, healthCheckService, tradingService, this),\n                interval: Config.PollingInterval * 1000 / Application.Speed,\n                startDelay: Constants.TaskDelays.ZeroDelay,\n                startTask: false,\n                runNow: true,\n                skipIteration: 0);\n\n            loggingService.Info(\"TradingViewCryptoSignalReceiver started\");\n        }\n\n        public void Stop()\n        {\n            loggingService.Info(\"Stop TradingViewCryptoSignalReceiver...\");\n\n            tasksService.RemoveTask($\"{nameof(TradingViewCryptoSignalPollingTimedTask)} [{SignalName}]\", stopTask: true);\n\n            healthCheckService.RemoveHealthCheck($\"{Constants.HealthChecks.TradingViewCryptoSignalsReceived} [{SignalName}]\");\n\n            loggingService.Info(\"TradingViewCryptoSignalReceiver stopped\");\n        }\n\n        public int GetPeriod()\n        {\n            return Config.SignalPeriod;\n        }\n\n        public IEnumerable<ISignal> GetSignals()\n        {\n            return tradingViewCryptoSignalPollingTimedTask?.GetSignals() ?? new List<ISignal>();\n        }\n\n        public double? GetAverageRating()\n        {\n            return tradingViewCryptoSignalPollingTimedTask?.GetAverageRating();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Signals.TradingView/TimedTasks/TradingViewCryptoSignalPollingTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Signals.Base;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Text;\n\nnamespace IntelliTrader.Signals.TradingView\n{\n    internal class TradingViewCryptoSignalPollingTimedTask : HighResolutionTimedTask\n    {\n        private const int HISTORICAL_SIGNALS_SNAPSHOT_MIN_INTERVAL_MILLISECONDS = 45000;\n        private const int HISTORICAL_SIGNALS_ADDITIONAL_SAVE_MINUTES = 5;\n        private const int HISTORICAL_SIGNALS_MAX_ADDITIONAL_ELAPSED_MINUTES = 1;\n\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITradingService tradingService;\n        private readonly TradingViewCryptoSignalReceiver signalReceiver;\n        private readonly JsonSerializer signalsSerializer;\n        private readonly HttpClient httpClient;\n\n        private readonly ConcurrentDictionary<DateTimeOffset, List<Signal>> signalsHistory = new ConcurrentDictionary<DateTimeOffset, List<Signal>>();\n        private DateTimeOffset lastSnapshotDate;\n        private List<Signal> signals;\n        private double? averageRating;\n\n        private object syncRoot = new object();\n\n        public TradingViewCryptoSignalPollingTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService,\n            ITradingService tradingService, TradingViewCryptoSignalReceiver signalReceiver)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tradingService = tradingService;\n            this.signalReceiver = signalReceiver;\n\n            this.signalsSerializer = new JsonSerializer();\n            this.signalsSerializer.Converters.Add(new TradingViewCryptoSignalConverter());\n            this.httpClient = CreateHttpClient();\n        }\n\n        protected override void Run()\n        {\n            var requestData = signalReceiver.Config.RequestData\n                .Replace(\"%EXCHANGE%\", tradingService.Config.Exchange.ToUpper())\n                .Replace(\"%MARKET%\", tradingService.Config.Market)\n                .Replace(\"%PERIOD%\", signalReceiver.Config.SignalPeriod <= 240 ? $\"|{signalReceiver.Config.SignalPeriod}\" : \"\")\n                .Replace(\"%VOLATILITY%\", $\".{signalReceiver.Config.VolatilityPeriod?[0] ?? 'W'}\");\n\n            var requestContent = new StringContent(requestData, Encoding.UTF8, \"application/json\");\n            try\n            {\n                using (var response = httpClient.PostAsync(signalReceiver.Config.RequestUrl, requestContent).Result)\n                {\n                    var responseContent = response.Content.ReadAsStringAsync().Result;\n                    var jtokens = JObject.Parse(responseContent).SelectTokens(\"data[*].d\");\n                    lock (syncRoot)\n                    {\n                        List<Signal> historicalSignals = GetHistoricalSignals();\n                        signals = jtokens.Select(t =>\n                        {\n                            try\n                            {\n                                var signal = t.ToObject<Signal>(signalsSerializer);\n                                if (signal.Pair.EndsWith(tradingService.Config.Market))\n                                {\n                                    signal.Name = signalReceiver.SignalName;\n\n                                    var historicalSignal = historicalSignals?.FirstOrDefault(s => s.Pair == signal.Pair);\n                                    if (historicalSignal != null)\n                                    {\n                                        signal.VolumeChange = CalculatePercentageChange(historicalSignal.Volume, signal.Volume);\n                                        signal.RatingChange = CalculatePercentageChange(historicalSignal.Rating, signal.Rating);\n                                    }\n                                    return signal;\n                                }\n                                else\n                                {\n                                    return null;\n                                }\n                            }\n                            catch (Exception ex)\n                            {\n                                loggingService.Debug(\"Unable to parse Trading View Crypto Signal\", ex);\n                                return null;\n                            }\n                        }).Where(s => s != null && s.Pair != null).ToList();\n\n                        if (signals.Count > 0)\n                        {\n                            if ((DateTimeOffset.Now - lastSnapshotDate).TotalMilliseconds > HISTORICAL_SIGNALS_SNAPSHOT_MIN_INTERVAL_MILLISECONDS)\n                            {\n                                signalsHistory.TryAdd(DateTimeOffset.Now, signals);\n                                lastSnapshotDate = DateTimeOffset.Now;\n                                CleanUpSignalsHistory();\n                            }\n                            averageRating = signals.Any(s => s.Rating != null) ? signals.Where(s => s.Rating != null).Average(s => s.Rating) : null;\n                            healthCheckService.UpdateHealthCheck($\"{Constants.HealthChecks.TradingViewCryptoSignalsReceived} [{signalReceiver.SignalName}]\", $\"Total: {signals.Count()}\");\n                        }\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                loggingService.Debug(\"Unable to retrieve TV Signals\", ex);\n            }\n        }\n\n        public IEnumerable<ISignal> GetSignals()\n        {\n            lock (syncRoot)\n            {\n                return signals;\n            }\n        }\n\n        public double? GetAverageRating()\n        {\n            lock (syncRoot)\n            {\n                return averageRating;\n            }\n        }\n\n        private List<Signal> GetHistoricalSignals()\n        {\n            lock (syncRoot)\n            {\n                foreach (var date in signalsHistory.Keys.OrderByDescending(d => d))\n                {\n                    double elapsedMinutes = (DateTimeOffset.Now - date).TotalMinutes;\n                    if (elapsedMinutes >= signalReceiver.Config.SignalPeriod && (elapsedMinutes - signalReceiver.Config.SignalPeriod) <= HISTORICAL_SIGNALS_MAX_ADDITIONAL_ELAPSED_MINUTES)\n                    {\n                        return signalsHistory[date];\n                    }\n                }\n                return null;\n            }\n        }\n\n        private void CleanUpSignalsHistory()\n        {\n            lock (syncRoot)\n            {\n                foreach (var date in signalsHistory.Keys)\n                {\n                    if ((DateTimeOffset.Now - date).TotalMinutes > signalReceiver.Config.SignalPeriod + HISTORICAL_SIGNALS_ADDITIONAL_SAVE_MINUTES)\n                    {\n                        signalsHistory.TryRemove(date, out List<Signal> signals);\n                    }\n                }\n            }\n        }\n\n        private double? CalculatePercentageChange(double? a, double? b)\n        {\n            if (a != null && b != null)\n            {\n                if (a == 0 && b == 0 || a == b)\n                {\n                    return 0;\n                }\n                else if (a == 0)\n                {\n                    return 100 * Math.Sign((double)b);\n                }\n                else if (b == 0)\n                {\n                    return -100 * Math.Sign((double)a);\n                }\n                else\n                {\n                    var change = Math.Abs((double)((b - a) / a * 100));\n                    return (a < b) ? change : change * -1;\n                }\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        private HttpClient CreateHttpClient()\n        {\n            var httpClient = new HttpClient();\n            httpClient.DefaultRequestHeaders.Accept.Clear();\n            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(\"application/json\"));\n            httpClient.DefaultRequestHeaders.Add(\"Cache-Control\", \"no-cache, no-store, max-age=0, must-revalidate\");\n            return httpClient;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/AppModule.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<TradingService>().As<ITradingService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.TradingService).SingleInstance();\n            builder.RegisterType<OrderingService>().As<IOrderingService>().SingleInstance();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/IntelliTrader.Trading.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n  </PropertyGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Exchange.Base\\IntelliTrader.Exchange.Base.csproj\" />\n    <ProjectReference Include=\"..\\IntelliTrader.Signals.Base\\IntelliTrader.Signals.Base.csproj\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "IntelliTrader.Trading/Models/Accounts/ExchangeAccount.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Newtonsoft.Json;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nnamespace IntelliTrader.Trading\n{\n    internal class ExchangeAccount : TradingAccountBase\n    {\n        private const string BACKUP_DIR = \"backup\";\n\n        public override bool IsVirtual => false;\n\n        public ExchangeAccount(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ISignalsService signalsService, ITradingService tradingService)\n            : base(loggingService, notificationService, healthCheckService, signalsService, tradingService)\n        {\n\n        }\n\n        public override void Refresh()\n        {\n            loggingService.Info(\"Refresh account...\");\n\n            decimal newBalance = 0;\n            Dictionary<string, decimal> availableAmounts = new Dictionary<string, decimal>();\n            Dictionary<string, IEnumerable<IOrderDetails>> availableTrades = new Dictionary<string, IEnumerable<IOrderDetails>>();\n            DateTimeOffset refreshStart = DateTimeOffset.Now;\n\n            // Preload account data without locking the account\n            try\n            {\n                loggingService.Info(\"Get account data...\");\n\n                foreach (var kvp in tradingService.Exchange.GetAvailableAmounts())\n                {\n                    string currency = kvp.Key;\n                    decimal amount = kvp.Value;\n                    string pair = currency + tradingService.Config.Market;\n\n                    if (currency == tradingService.Config.Market)\n                    {\n                        newBalance = amount;\n                    }\n                    else if (!tradingService.Config.ExcludedPairs.Contains(pair))\n                    {\n                        try\n                        {\n                            IEnumerable<IOrderDetails> trades = tradingService.Exchange.GetTrades(pair);\n                            availableTrades.Add(pair, trades);\n                            availableAmounts.Add(pair, amount);\n                        }\n                        catch (Exception ex) when (ex.Message != null && ex.Message.Contains(\"Invalid symbol\"))\n                        {\n                            loggingService.Info($\"Skip invalid pair: {pair}\");\n                        }\n                    }\n                }\n\n                loggingService.Info(\"Account data retrieved\");\n            }\n            catch (Exception ex) when (!isInitialRefresh)\n            {\n                healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, ex.Message, true);\n                loggingService.Error(\"Unable to get account data\", ex);\n                notificationService.Notify(\"Unable to get account data\");\n                return;\n            }\n\n            // Lock the account and reapply all trades\n            try\n            {\n                lock (SyncRoot)\n                {\n                    ConcurrentDictionary<string, TradingPair> tradingPairsSaved = null;\n                    if (isInitialRefresh)\n                    {\n                        TradingAccountData data = LoadSavedData();\n                        tradingPairsSaved = data?.TradingPairs ?? new ConcurrentDictionary<string, TradingPair>();\n                    }\n                    else\n                    {\n                        tradingPairsSaved = tradingPairs;\n                    }\n                    tradingPairs = new ConcurrentDictionary<string, TradingPair>();\n\n                    foreach (var kvp in availableTrades)\n                    {\n                        string pair = kvp.Key;\n                        decimal amount = availableAmounts[pair];\n                        IEnumerable<IOrderDetails> trades = kvp.Value;\n\n                        foreach (var trade in trades)\n                        {\n                            if (trade.Date >= tradingService.Config.AccountInitialBalanceDate)\n                            {\n                                if (trade.Side == OrderSide.Buy)\n                                {\n                                    AddBuyOrder(trade);\n                                }\n                                else\n                                {\n                                    ITradeResult tradeResult = AddSellOrder(trade);\n                                }\n\n                                if (isInitialRefresh)\n                                {\n                                    tradingService.LogOrder(trade);\n                                }\n                            }\n                        }\n\n                        if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && tradingPair.Amount != amount)\n                        {\n                            loggingService.Info($\"Adjust amount for {pair}: {tradingPair.Amount:0.########} => {amount:0.########}\");\n                            tradingPair.Amount = amount;\n                        }\n                    }\n\n                    foreach (var pair in tradingPairs.Keys.ToList())\n                    {\n                        if (tradingPairsSaved.TryGetValue(pair, out TradingPair saved))\n                        {\n                            tradingPairs[pair].Metadata = saved.Metadata ?? new OrderMetadata();\n                        }\n                    }\n\n                    balance = newBalance;\n\n                    // Add trades that were completed during account refresh\n                    foreach (var order in tradingService.OrderHistory)\n                    {\n                        if (order.Date > refreshStart)\n                        {\n                            if (tradingPairs.TryGetValue(order.Pair, out TradingPair tradingPair))\n                            {\n                                if (!tradingPair.OrderIds.Contains(order.OrderId))\n                                {\n                                    loggingService.Info($\"Add missing order for {order.Pair} ({order.OrderId})\");\n                                    AddOrder(order);\n                                }\n                            }\n                            else\n                            {\n                                loggingService.Info($\"Add missing order for {order.Pair} ({order.OrderId})\");\n                                AddOrder(order);\n                            }\n                        }\n                    }\n\n                    if (isInitialRefresh)\n                    {\n                        isInitialRefresh = false;\n                    }\n\n                    loggingService.Info($\"Account refreshed. Balance: {balance}, Trading pairs: {tradingPairs.Count}\");\n                    healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, $\"Balance: {balance}, Trading pairs: {tradingPairs.Count}\");\n                }\n            }\n            catch (Exception ex)\n            {\n                tradingPairs.Clear();\n                tradingService.SuspendTrading();\n                healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, ex.Message, true);\n                loggingService.Error(\"Unable to refresh account\", ex);\n                notificationService.Notify(\"Unable to refresh account\");\n            }\n        }\n\n        public override void Save()\n        {\n            lock (SyncRoot)\n            {\n                try\n                {\n                    var tradingService = Application.Resolve<ITradingService>();\n                    string accountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.AccountFilePath);\n\n                    var data = new TradingAccountData\n                    {\n                        Balance = balance,\n                        TradingPairs = tradingPairs,\n                    };\n\n                    string accountJson = JsonConvert.SerializeObject(data, Formatting.Indented);\n                    var accountFile = new FileInfo(accountFilePath);\n                    accountFile.Directory.Create();\n                    File.WriteAllText(accountFile.FullName, accountJson);\n                }\n                catch (Exception ex)\n                {\n                    loggingService.Error(\"Unable to save account data\", ex);\n                }\n            }\n        }\n\n        private TradingAccountData LoadSavedData()\n        {\n            lock (SyncRoot)\n            {\n                try\n                {\n                    var tradingService = Application.Resolve<ITradingService>();\n                    string accountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.AccountFilePath);\n\n                    if (File.Exists(accountFilePath))\n                    {\n                        BackupAccountData(accountFilePath);\n                        string accountJson = File.ReadAllText(accountFilePath);\n                        return JsonConvert.DeserializeObject<TradingAccountData>(accountJson);\n                    }\n                    else\n                    {\n                        return null;\n                    }\n                }\n                catch (Exception ex)\n                {\n                    loggingService.Error(\"Unable to load account data\", ex);\n                    return null;\n                }\n            }\n        }\n\n        private void BackupAccountData(string accountFilePath)\n        {\n            try\n            {\n                string backupAccountFileName = DateTimeOffset.UtcNow.ToString(\"yyyy-MM-dd-HH-mm-ss\") + \".json\";\n                string backupAccountFilePath = Path.Combine(Path.GetDirectoryName(accountFilePath), BACKUP_DIR, backupAccountFileName);\n                var backupAccountFile = new FileInfo(backupAccountFilePath);\n                backupAccountFile.Directory.Create();\n                File.Copy(accountFilePath, backupAccountFile.FullName);\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error(\"Unable to save account backup data\", ex);\n            }\n        }\n        public override void Dispose()\n        {\n            base.Dispose();\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.AccountRefreshed);\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/Accounts/TradingAccountBase.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Exchange.Base;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    internal abstract class TradingAccountBase : ITradingAccount\n    {\n        public object SyncRoot { get; private set; } = new object();\n        public abstract bool IsVirtual { get; }\n\n        protected readonly ILoggingService loggingService;\n        protected readonly INotificationService notificationService;\n        protected readonly IHealthCheckService healthCheckService;\n        protected readonly ISignalsService signalsService;\n        protected readonly ITradingService tradingService;\n\n        protected bool isInitialRefresh = true;\n        protected decimal balance;\n        protected ConcurrentDictionary<string, TradingPair> tradingPairs = new ConcurrentDictionary<string, TradingPair>();\n\n        public TradingAccountBase(ILoggingService loggingService, INotificationService notificationService,\n            IHealthCheckService healthCheckService, ISignalsService signalsService, ITradingService tradingService)\n        {\n            this.loggingService = loggingService;\n            this.notificationService = notificationService;\n            this.healthCheckService = healthCheckService;\n            this.signalsService = signalsService;\n            this.tradingService = tradingService;\n        }\n\n        public abstract void Refresh();\n\n        public abstract void Save();\n\n        public virtual void AddOrder(IOrderDetails order)\n        {\n            if (order.Side == OrderSide.Buy)\n            {\n                AddBuyOrder(order);\n            }\n            else\n            {\n                AddSellOrder(order);\n            }\n        }\n\n        public virtual void AddBuyOrder(IOrderDetails order)\n        {\n            lock (SyncRoot)\n            {\n                if (order.Side == OrderSide.Buy && (order.Result == OrderResult.Filled || order.Result == OrderResult.FilledPartially))\n                {\n                    string feesPair = order.FeesCurrency + tradingService.Config.Market;\n                    decimal feesPairCurrency = (feesPair == order.Pair) ? order.Fees : 0;\n                    decimal feesMarketCurrency = tradingService.CalculateOrderFees(order);\n                    decimal balanceOffset = -feesMarketCurrency;\n\n                    if (!order.IsNormalized || order.Pair.EndsWith(Constants.Markets.USDT))\n                    {\n                        balanceOffset -= order.Cost;\n                        AddBalance(balanceOffset);\n                    }\n                    else\n                    {\n                        string normalizedMarket = tradingService.Exchange.GetPairMarket(order.OriginalPair) == Constants.Markets.USDT ?\n                            tradingService.Config.Market + tradingService.Exchange.GetPairMarket(order.OriginalPair) :\n                            tradingService.Exchange.GetPairMarket(order.OriginalPair) + tradingService.Config.Market;\n\n                        if (tradingPairs.TryGetValue(normalizedMarket, out TradingPair normalizedMarketPair))\n                        {\n                            if (normalizedMarketPair.Cost > order.Cost)\n                            {\n                                decimal amount = order.Cost / tradingService.GetPrice(normalizedMarket, TradePriceType.Bid);\n                                normalizedMarketPair.Amount -= amount;\n                                if (normalizedMarketPair.Amount <= 0)\n                                {\n                                    tradingPairs.TryRemove(normalizedMarket, out normalizedMarketPair);\n                                    if (normalizedMarketPair.Amount < 0)\n                                    {\n                                        loggingService.Error($\"Normalized pair {normalizedMarket} has negative amount: {normalizedMarketPair.Amount}\");\n                                    }\n                                }\n                            }\n                            else\n                            {\n                                tradingPairs.TryRemove(normalizedMarket, out normalizedMarketPair);\n                            }\n                        }\n                        else\n                        {\n                            loggingService.Error($\"Unable to get normalized pair {normalizedMarketPair}\");\n                        }\n                    }\n\n                    AddOrUpdatePair(order, order.Pair, feesMarketCurrency, feesPairCurrency);\n                }\n            }\n        }\n\n        public virtual ITradeResult AddSellOrder(IOrderDetails order)\n        {\n            ITradeResult tradeResult = new TradeResult();\n            lock (SyncRoot)\n            {\n                if (tradingPairs.TryGetValue(order.Pair, out TradingPair tradingPair))\n                {\n                    if (order.Side == OrderSide.Sell && (order.Result == OrderResult.Filled || order.Result == OrderResult.FilledPartially))\n                    {\n                        string feesPair = order.FeesCurrency + tradingService.Config.Market;\n                        decimal feesPairCurrency = (feesPair == order.Pair) ? order.Fees : 0;\n                        decimal feesMarketCurrency = tradingService.CalculateOrderFees(order);\n                        decimal amountDifference = order.AmountFilled / tradingPair.Amount;\n                        decimal balanceOffset = -feesMarketCurrency;\n\n                        if (!order.IsNormalized || order.Pair.EndsWith(Constants.Markets.USDT))\n                        {\n                            balanceOffset += order.Cost;\n                            AddBalance(balanceOffset);\n                        }\n                        else\n                        {\n                            string normalizedMarket = tradingService.Exchange.GetPairMarket(order.OriginalPair) == Constants.Markets.USDT ?\n                                tradingService.Config.Market + tradingService.Exchange.GetPairMarket(order.OriginalPair) :\n                                tradingService.Exchange.GetPairMarket(order.OriginalPair) + tradingService.Config.Market;\n\n                            decimal price = tradingService.GetPrice(normalizedMarket, TradePriceType.Ask);\n                            decimal amount = (order.Cost - feesMarketCurrency) / price;\n                            AddOrUpdatePair(order, normalizedMarket, feesMarketCurrency, feesPairCurrency, amount, price);\n                        }\n\n                        decimal sellFees = feesMarketCurrency + tradingPair.Fees * amountDifference;\n                        tradingPair.Fees += feesMarketCurrency - sellFees;\n                        decimal costDifference = order.Cost - tradingPair.GetPartialCost(order.AmountFilled) - (tradingPair.Metadata.AdditionalCosts ?? 0);\n                        decimal profit = costDifference * amountDifference - feesMarketCurrency;\n\n                        if (tradingPair.Amount > order.AmountFilled)\n                        {\n                            tradingPair.Amount -= order.AmountFilled;\n                            if (tradingPair.CurrentCost < tradingService.Config.MinCost)\n                            {\n                                tradingPair.OrderDates.Clear();\n                            }\n                        }\n                        else\n                        {\n                            tradingPairs.TryRemove(order.Pair, out tradingPair);\n                        }\n\n                        tradeResult = new TradeResult\n                        {\n                            IsSuccessful = true,\n                            Pair = order.Pair,\n                            Amount = order.AmountFilled,\n                            OrderDates = tradingPair.OrderDates,\n                            AveragePrice = tradingPair.AveragePrice,\n                            Fees = sellFees,\n                            SellDate = order.Date,\n                            SellPrice = order.AveragePrice,\n                            BalanceOffset = balanceOffset,\n                            Profit = profit,\n                            Metadata = order.Metadata,\n                        };\n                    }\n                }\n            }\n            return tradeResult;\n        }\n\n        public ITradingPair AddOrUpdatePair(IOrderDetails order, string pair, decimal feesMarketCurrency, decimal feesPairCurrency, decimal? amountOverride = null, decimal? averagePriceOverride = null)\n        {\n            decimal amount = amountOverride ?? order.AmountFilled;\n            decimal amountAfterFees = amount - feesPairCurrency;\n            decimal averagePrice = averagePriceOverride ?? (order.AveragePrice + (feesMarketCurrency / amount));\n\n            if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair))\n            {\n                if (!tradingPair.OrderIds.Contains(order.OrderId))\n                {\n                    tradingPair.OrderIds.Add(order.OrderId);\n                    tradingPair.OrderDates.Add(order.Date);\n                }\n                tradingPair.AveragePrice = (tradingPair.Cost + amountAfterFees * averagePrice) / (tradingPair.Amount + amountAfterFees);\n                tradingPair.Amount += amountAfterFees;\n                tradingPair.Fees += feesMarketCurrency;\n                tradingPair.SetMetadata(tradingPair.Metadata.MergeWith(order.Metadata));\n            }\n            else\n            {\n                tradingPair = new TradingPair\n                {\n                    Pair = pair,\n                    OrderIds = new List<string> { order.OrderId },\n                    OrderDates = new List<DateTimeOffset> { order.Date },\n                    AveragePrice = averagePrice,\n                    Amount = amountAfterFees,\n                    Fees = feesMarketCurrency,\n                    Metadata = order.Metadata\n                };\n                tradingPairs.TryAdd(pair, tradingPair);\n                tradingPair.SetCurrentValues(tradingService.GetPrice(tradingPair.Pair), tradingService.Exchange.GetPriceSpread(tradingPair.Pair));\n                tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null ? signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null;\n                tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating();\n                if (tradingPair.Metadata.LastBuyMargin == null)\n                {\n                    tradingPair.Metadata.LastBuyMargin = tradingPair.CurrentMargin;\n                }\n            }\n            return tradingPair;\n        }\n\n        public IOrderDetails AddBlankOrder(string pair, decimal amount, bool includeFees = true)\n        {\n            lock (SyncRoot)\n            {\n                if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && tradingPair.Amount >= amount)\n                {\n                    if (tradingPair.Amount > amount)\n                    {\n                        return new OrderDetails\n                        {\n                            OrderId = DateTime.Now.ToFileTimeUtc().ToString(),\n                            Side = OrderSide.Sell,\n                            Result = OrderResult.Filled,\n                            Date = DateTimeOffset.Now,\n                            Pair = pair,\n                            Amount = amount,\n                            AmountFilled = amount,\n                            Price = tradingPair.AveragePrice,\n                            AveragePrice = tradingPair.AveragePrice,\n                            Fees = includeFees ? tradingPair.Fees * (amount / tradingPair.Amount) : 0,\n                            FeesCurrency = includeFees ? tradingService.Config.Market : null,\n                            Metadata = tradingPair.Metadata\n                        };\n                    }\n                    else\n                    {\n                        return new OrderDetails();\n                    }\n                }\n                else\n                {\n                    return new OrderDetails();\n                }\n            }\n        }\n\n        public void AddBalance(decimal balanceOffset)\n        {\n            balance += balanceOffset;\n        }\n\n        public decimal GetBalance()\n        {\n            lock (SyncRoot)\n            {\n                return balance;\n            }\n        }\n\n        public decimal GetTotalBalance()\n        {\n            decimal totalBalance = balance;\n            foreach (var tradingPair in tradingPairs.Values)\n            {\n                totalBalance += tradingService.GetPrice(tradingPair.Pair, TradePriceType.Bid) * tradingPair.Amount;\n            }\n            return totalBalance;\n        }\n\n        public bool HasTradingPair(string pair, bool includeDust = false)\n        {\n            lock (SyncRoot)\n            {\n                if (includeDust)\n                {\n                    return tradingPairs.ContainsKey(pair);\n                }\n                else\n                {\n                    return tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && (tradingPair.CurrentCost > tradingService.Config.MinCost || tradingPair.CurrentPrice == 0);\n                }\n            }\n        }\n\n        public ITradingPair GetTradingPair(string pair, bool includeDust = false)\n        {\n            lock (SyncRoot)\n            {\n                if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && (includeDust || tradingPair.CurrentCost > tradingService.Config.MinCost || tradingPair.CurrentPrice == 0))\n                {\n                    return tradingPair;\n                }\n                else\n                {\n                    return null;\n                }\n            }\n        }\n\n        public IEnumerable<ITradingPair> GetTradingPairs(bool includeDust = false)\n        {\n            lock (SyncRoot)\n            {\n                if (includeDust)\n                {\n                    return tradingPairs.Values;\n                }\n                else\n                {\n                    return tradingPairs.Values.Where(t => t.CurrentCost > tradingService.Config.MinCost || t.CurrentPrice == 0);\n                }\n            }\n        }\n\n        public virtual void Dispose()\n        {\n            lock (SyncRoot)\n            {\n                tradingPairs.Clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/Accounts/TradingAccountData.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Newtonsoft.Json;\nusing System.Collections.Concurrent;\n\nnamespace IntelliTrader.Trading\n{\n    internal class TradingAccountData\n    {\n        [JsonConverter(typeof(DecimalFormatJsonConverter), 8)]\n        public decimal Balance { get; set; }\n        public ConcurrentDictionary<string, TradingPair> TradingPairs { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/Accounts/VirtualAccount.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.IO;\nusing Newtonsoft.Json;\nusing IntelliTrader.Core;\n\nnamespace IntelliTrader.Trading\n{\n    internal class VirtualAccount : TradingAccountBase\n    {\n        public override bool IsVirtual => true;\n\n        public VirtualAccount(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ISignalsService signalsService, ITradingService tradingService)\n            : base(loggingService, notificationService, healthCheckService, signalsService, tradingService)\n        {\n\n        }\n\n        public override void Refresh()\n        {\n            lock (SyncRoot)\n            {\n                // Only done once, since all the data is always up to date\n                if (isInitialRefresh)\n                {\n                    Load();\n                    isInitialRefresh = false;\n                }\n\n                healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed);\n            }\n        }\n\n        public override void Save()\n        {\n            lock (SyncRoot)\n            {\n                var tradingService = Application.Resolve<ITradingService>();\n                string virtualAccountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath);\n\n                var data = new TradingAccountData\n                {\n                    Balance = balance,\n                    TradingPairs = tradingPairs\n                };\n\n                string virtualAccountJson = JsonConvert.SerializeObject(data, Formatting.Indented);\n                var virtualAccountFile = new FileInfo(virtualAccountFilePath);\n                virtualAccountFile.Directory.Create();\n                File.WriteAllText(virtualAccountFile.FullName, virtualAccountJson);\n            }\n        }\n\n        public void Load()\n        {\n            lock (SyncRoot)\n            {\n                var tradingService = Application.Resolve<ITradingService>();\n                var virtualAccountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath);\n\n                if (File.Exists(virtualAccountFilePath))\n                {\n                    string virtualAccountJson = File.ReadAllText(virtualAccountFilePath);\n                    var virtualAccountData = JsonConvert.DeserializeObject<TradingAccountData>(virtualAccountJson);\n\n                    balance = virtualAccountData.Balance;\n                    tradingPairs = virtualAccountData.TradingPairs;\n                }\n                else\n                {\n                    balance = tradingService.Config.VirtualAccountInitialBalance;\n                    tradingPairs = new ConcurrentDictionary<string, TradingPair>();\n                }\n            }\n        }\n\n        public override void Dispose()\n        {\n            base.Dispose();\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.AccountRefreshed);\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/BuyTrailingInfo.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    internal class BuyTrailingInfo : TrailingInfo\n    {\n        public BuyOptions BuyOptions { get; set; }\n        public BuyTrailingStopAction TrailingStopAction { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/Config/TradingConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace IntelliTrader.Trading\n{\n    internal class TradingConfig : ITradingConfig\n    {\n        public bool Enabled { get; set; }\n        public string Market { get; set; }\n        public string Exchange { get; set; }\n        public int MaxPairs { get; set; }\n        public decimal MinCost { get; set; }\n        public List<string> ExcludedPairs { get; set; }\n        public TradePriceType TradePriceType { get; set; }\n\n        public bool BuyEnabled { get; set; }\n        public OrderType BuyType { get; set; }\n        public decimal BuyMaxCost { get; set; }\n        public decimal BuyMultiplier { get; set; }\n        public decimal BuyMinBalance { get; set; }\n        public double BuySamePairTimeout { get; set; }\n        public decimal BuyTrailing { get; set; }\n        public decimal BuyTrailingStopMargin { get; set; }\n        public BuyTrailingStopAction BuyTrailingStopAction { get; set; }\n\n        public bool BuyDCAEnabled { get; set; }\n        public decimal BuyDCAMultiplier { get; set; }\n        public decimal BuyDCAMinBalance { get; set; }\n        public double BuyDCASamePairTimeout { get; set; }\n        public decimal BuyDCATrailing { get; set; }\n        public decimal BuyDCATrailingStopMargin { get; set; }\n        public BuyTrailingStopAction BuyDCATrailingStopAction { get; set; }\n\n        public bool SellEnabled { get; set; }\n        public OrderType SellType { get; set; }\n        public decimal SellMargin { get; set; }\n        public decimal SellTrailing { get; set; }\n        public decimal SellTrailingStopMargin { get; set; }\n        public SellTrailingStopAction SellTrailingStopAction { get; set; }\n        public bool SellStopLossEnabled { get; set; }\n        public bool SellStopLossAfterDCA { get; set; }\n        public double SellStopLossMinAge { get; set; }\n        public decimal SellStopLossMargin { get; set; }\n\n        public decimal SellDCAMargin { get; set; }\n        public decimal SellDCATrailing { get; set; }\n        public decimal SellDCATrailingStopMargin { get; set; }\n        public SellTrailingStopAction SellDCATrailingStopAction { get; set; }\n\n        public bool RepeatLastDCALevel { get; set; }\n        public List<DCALevel> DCALevels { get; set; }\n\n        public double TradingCheckInterval { get; set; }\n        public double AccountRefreshInterval { get; set; }\n        public decimal AccountInitialBalance { get; set; }\n        public DateTimeOffset AccountInitialBalanceDate { get; set; }\n        public string AccountFilePath { get; set; }\n\n        public bool VirtualTrading { get; set; }\n        public decimal VirtualTradingFees { get; set; }\n        public decimal VirtualAccountInitialBalance { get; set; }\n        public string VirtualAccountFilePath { get; set; }\n\n        public ITradingConfig Clone()\n        {\n            return new TradingConfig\n            {\n                Enabled = Enabled,\n                Market = Market,\n                Exchange = Exchange,\n                MaxPairs = MaxPairs,\n                MinCost = MinCost,\n                ExcludedPairs = ExcludedPairs,\n                TradePriceType = TradePriceType,\n\n                BuyEnabled = BuyEnabled,\n                BuyType = BuyType,\n                BuyMaxCost = BuyMaxCost,\n                BuyMultiplier = BuyMultiplier,\n                BuyMinBalance = BuyMinBalance,\n                BuySamePairTimeout = BuySamePairTimeout,\n                BuyTrailing = BuyTrailing,\n                BuyTrailingStopMargin = BuyTrailingStopMargin,\n                BuyTrailingStopAction = BuyTrailingStopAction,\n\n                BuyDCAEnabled = BuyDCAEnabled,\n                BuyDCAMultiplier = BuyDCAMultiplier,\n                BuyDCAMinBalance = BuyDCAMinBalance,\n                BuyDCASamePairTimeout = BuyDCASamePairTimeout,\n                BuyDCATrailing = BuyDCATrailing,\n                BuyDCATrailingStopMargin = BuyDCATrailingStopMargin,\n                BuyDCATrailingStopAction = BuyDCATrailingStopAction,\n\n                SellEnabled = SellEnabled,\n                SellType = SellType,\n                SellMargin = SellMargin,\n                SellTrailing = SellTrailing,\n                SellTrailingStopMargin = SellTrailingStopMargin,\n                SellTrailingStopAction = SellTrailingStopAction,\n                SellStopLossEnabled = SellStopLossEnabled,\n                SellStopLossAfterDCA = SellStopLossAfterDCA,\n                SellStopLossMinAge = SellStopLossMinAge,\n                SellStopLossMargin = SellStopLossMargin,\n\n                SellDCAMargin = SellDCAMargin,\n                SellDCATrailing = SellDCATrailing,\n                SellDCATrailingStopMargin = SellDCATrailingStopMargin,\n                SellDCATrailingStopAction = SellDCATrailingStopAction,\n\n                RepeatLastDCALevel = RepeatLastDCALevel,\n                DCALevels = DCALevels,\n\n                TradingCheckInterval = TradingCheckInterval,\n                AccountRefreshInterval = AccountRefreshInterval,\n                AccountInitialBalance = AccountInitialBalance,\n                AccountInitialBalanceDate = AccountInitialBalanceDate,\n                AccountFilePath = AccountFilePath,\n\n                VirtualTrading = VirtualTrading,\n                VirtualTradingFees = VirtualTradingFees,\n                VirtualAccountInitialBalance = VirtualAccountInitialBalance,\n                VirtualAccountFilePath = VirtualAccountFilePath\n            };\n        }\n    }\n}"
  },
  {
    "path": "IntelliTrader.Trading/Models/PairConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    internal class PairConfig : IPairConfig\n    {\n        public IEnumerable<string> Rules { get; set; }\n\n        public int MaxPairs { get; set; }\n\n        public bool BuyEnabled { get; set; }\n        public OrderType BuyType { get; set; }\n        public decimal BuyMaxCost { get; set; }\n        public decimal BuyMultiplier { get; set; }\n        public decimal BuyMinBalance { get; set; }\n        public double BuySamePairTimeout { get; set; }\n        public decimal BuyTrailing { get; set; }\n        public decimal BuyTrailingStopMargin { get; set; }\n        public BuyTrailingStopAction BuyTrailingStopAction { get; set; }\n\n        public bool SellEnabled { get; set; }\n        public OrderType SellType { get; set; }\n        public decimal SellMargin { get; set; }\n        public decimal SellTrailing { get; set; }\n        public decimal SellTrailingStopMargin { get; set; }\n        public SellTrailingStopAction SellTrailingStopAction { get; set; }\n        public bool SellStopLossEnabled { get; set; }\n        public bool SellStopLossAfterDCA { get; set; }\n        public double SellStopLossMinAge { get; set; }\n        public decimal SellStopLossMargin { get; set; }\n\n        public bool SwapEnabled { get; set; }\n        public List<string> SwapSignalRules { get; set; }\n        public int SwapTimeout { get; set; }\n\n        public bool ArbitrageEnabled { get; set; }\n        public List<ArbitrageMarket> ArbitrageMarkets { get; set; }\n        public ArbitrageType? ArbitrageType { get; set; }\n        public decimal? ArbitrageBuyMultiplier { get; set; }\n        public decimal? ArbitrageSellMultiplier { get; set; }\n        public List<string> ArbitrageSignalRules { get; set; }\n\n        public decimal? CurrentDCAMargin { get; set; }\n        public decimal? NextDCAMargin { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/SellTrailingInfo.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    internal class SellTrailingInfo : TrailingInfo\n    {\n        public SellOptions SellOptions { get; set; }\n        public SellTrailingStopAction TrailingStopAction { get; set; }\n        public decimal SellMargin { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/TradingPair.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace IntelliTrader.Trading\n{\n    public class TradingPair : ITradingPair\n    {\n        public string Pair { get; set; }\n        [JsonIgnore]\n        public string FormattedName => DCALevel > 0 ? $\"{Pair}({DCALevel})\" : Pair;\n        public int DCALevel => (OrderDates.Count > 0 ? (OrderDates.Count - 1) : 0) + (Metadata.AdditionalDCALevels ?? 0);\n        public List<string> OrderIds { get; set; }\n        public List<DateTimeOffset> OrderDates { get; set; }\n        [JsonConverter(typeof(DecimalFormatJsonConverter), 8)]\n        public decimal Amount { get; set; }\n        [JsonConverter(typeof(DecimalFormatJsonConverter), 8)]\n        public decimal AveragePrice { get; set; }\n        [JsonConverter(typeof(DecimalFormatJsonConverter), 8)]\n        public decimal Fees { get; set; }\n        [JsonConverter(typeof(DecimalFormatJsonConverter), 8)]\n        public decimal Cost => GetPartialCost(Amount);\n        [JsonIgnore]\n        public decimal? CostOverride { get; set; }\n        [JsonIgnore]\n        public decimal CurrentCost => CurrentPrice * Amount;\n        [JsonIgnore]\n        public decimal CurrentPrice { get; set; }\n        [JsonIgnore]\n        public decimal CurrentSpread { get; set; }\n        [JsonIgnore]\n        public decimal CurrentMargin => Utils.CalculatePercentage(Cost + Fees + (Metadata.AdditionalCosts ?? 0), CurrentCost);\n        [JsonIgnore]\n        public double CurrentAge => OrderDates != null && OrderDates.Count > 0 ? (DateTimeOffset.Now - OrderDates.Min()).TotalDays : 0;\n        [JsonIgnore]\n        public double LastBuyAge => OrderDates != null && OrderDates.Count > 0 ? (DateTimeOffset.Now - OrderDates.Max()).TotalDays : 0;\n        public OrderMetadata Metadata { get; set; } = new OrderMetadata();\n\n        public decimal GetPartialCost(decimal partialAmount)\n        {\n            if (CostOverride != null)\n            {\n                return CostOverride.Value;\n            }\n            else\n            {\n                return AveragePrice * partialAmount;\n            }\n        }\n\n        public void OverrideCost(decimal? costOverride)\n        {\n            CostOverride = costOverride;\n        }\n\n        public void SetCurrentValues(decimal currentPrice, decimal currentSpread)\n        {\n            CurrentPrice = currentPrice;\n            CurrentSpread = currentSpread;\n        }\n\n        public void SetMetadata(OrderMetadata metadata)\n        {\n            this.Metadata = metadata;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/TradingRuleModifiers.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    internal class TradingRuleModifiers\n    {\n        public int? MaxPairs { get; set; }\n\n        public bool? BuyEnabled { get; set; }\n        public decimal? BuyMaxCost { get; set; }\n        public decimal? BuyMultiplier { get; set; }\n        public decimal? BuyMinBalance { get; set; }\n        public double? BuySamePairTimeout { get; set; }\n        public decimal? BuyTrailing { get; set; }\n        public decimal? BuyTrailingStopMargin { get; set; }\n        public BuyTrailingStopAction? BuyTrailingStopAction { get; set; }\n\n        public bool? BuyDCAEnabled { get; set; }\n        public decimal? BuyDCAMultiplier { get; set; }\n        public decimal? BuyDCAMinBalance { get; set; }\n        public double? BuyDCASamePairTimeout { get; set; }\n        public decimal? BuyDCATrailing { get; set; }\n        public decimal? BuyDCATrailingStopMargin { get; set; }\n        public BuyTrailingStopAction? BuyDCATrailingStopAction { get; set; }\n\n        public bool? SellEnabled { get; set; }\n        public decimal? SellMargin { get; set; }\n        public decimal? SellTrailing { get; set; }\n        public decimal? SellTrailingStopMargin { get; set; }\n        public SellTrailingStopAction? SellTrailingStopAction { get; set; }\n        public bool? SellStopLossEnabled { get; set; }\n        public bool? SellStopLossAfterDCA { get; set; }\n        public double? SellStopLossMinAge { get; set; }\n        public decimal? SellStopLossMargin { get; set; }\n\n        public decimal? SellDCAMargin { get; set; }\n        public decimal? SellDCATrailing { get; set; }\n        public decimal? SellDCATrailingStopMargin { get; set; }\n        public SellTrailingStopAction? SellDCATrailingStopAction { get; set; }\n\n        public bool? RepeatLastDCALevel { get; set; }\n        public List<DCALevel> DCALevels { get; set; }\n\n        public bool? SwapEnabled { get; set; }\n        public List<string> SwapSignalRules { get; set; }\n        public int? SwapTimeout { get; set; }\n\n        public bool? ArbitrageEnabled { get; set; }\n        public List<ArbitrageMarket> ArbitrageMarkets { get; set; }\n        public ArbitrageType? ArbitrageType { get; set; }\n        public decimal? ArbitrageBuyMultiplier { get; set; }\n        public decimal? ArbitrageSellMultiplier { get; set; }\n        public List<String> ArbitrageSignalRules { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/TradingRulesConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    internal class TradingRulesConfig\n    {\n        public RuleProcessingMode ProcessingMode { get; set; }\n        public double CheckInterval { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Models/TrailingInfo.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Trading\n{\n    internal abstract class TrailingInfo\n    {\n        public decimal Trailing { get; set; }\n        public decimal TrailingStopMargin { get; set; }\n        public decimal InitialPrice { get; set; }\n        public decimal LastTrailingMargin { get; set; }\n        public decimal BestTrailingMargin { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Services/OrderingService.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Exchange.Base;\nusing System;\nusing System.Linq;\n\nnamespace IntelliTrader.Trading\n{\n    internal class OrderingService : IOrderingService\n    {\n        private readonly ILoggingService loggingService;\n        private readonly INotificationService notificationService;\n        private readonly ITradingService tradingService;\n\n        public OrderingService(ILoggingService loggingService, INotificationService notificationService, ITradingService tradingService)\n        {\n            this.loggingService = loggingService;\n            this.notificationService = notificationService;\n            this.tradingService = tradingService;\n        }\n\n        public IOrderDetails PlaceBuyOrder(BuyOptions options)\n        {\n            OrderDetails orderDetails = new OrderDetails();\n            tradingService.StopTrailingBuy(options.Pair);\n            tradingService.StopTrailingSell(options.Pair);\n\n            try\n            {\n                ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair, includeDust: true);\n                options.Price = tradingService.GetPrice(options.Pair, TradePriceType.Ask, normalize: false);\n                options.Amount = options.Amount ?? (options.MaxCost.Value / (options.Pair.EndsWith(Constants.Markets.USDT) ? 1 : options.Price));\n                options.Price = tradingService.Exchange.ClampOrderPrice(options.Pair, options.Price.Value);\n                options.Amount = tradingService.Exchange.ClampOrderAmount(options.Pair, options.Amount.Value);\n\n                if (tradingService.CanBuy(options, out string message))\n                {\n                    IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);\n                    BuyOrder buyOrder = new BuyOrder\n                    {\n                        Type = pairConfig.BuyType,\n                        Date = DateTimeOffset.Now,\n                        Pair = options.Pair,\n                        Price = options.Price.Value,\n                        Amount = options.Amount.Value\n                    };\n\n                    lock (tradingService.Account.SyncRoot)\n                    {\n                        loggingService.Info($\"Place buy order for {tradingPair?.FormattedName ?? options.Pair}. \" +\n                            $\"Price: {buyOrder.Price:0.00000000}, Amount: {buyOrder.Amount:0.########}, Signal Rule: \" + (options.Metadata.SignalRule ?? \"N/A\"));\n\n                        if (!tradingService.Config.VirtualTrading)\n                        {\n                            orderDetails = tradingService.Exchange.PlaceOrder(buyOrder) as OrderDetails;\n                        }\n                        else\n                        {\n                            string pairMarket = tradingService.Exchange.GetPairMarket(options.Pair);\n                            orderDetails = new OrderDetails\n                            {\n                                OrderId = DateTime.Now.ToFileTimeUtc().ToString(),\n                                Side = OrderSide.Buy,\n                                Result = OrderResult.Filled,\n                                Date = buyOrder.Date,\n                                Pair = buyOrder.Pair,\n                                Amount = buyOrder.Amount,\n                                AmountFilled = buyOrder.Amount,\n                                Price = buyOrder.Price,\n                                AveragePrice = buyOrder.Price,\n                                Fees = buyOrder.Amount * buyOrder.Price * tradingService.Config.VirtualTradingFees,\n                                FeesCurrency = pairMarket\n                            };\n                        }\n\n                        NormalizeOrder(orderDetails, TradePriceType.Ask);\n                        options.Metadata.TradingRules = pairConfig.Rules.ToList();\n                        options.Metadata.LastBuyMargin = options.Metadata.LastBuyMargin ?? tradingPair?.CurrentMargin ?? null;\n                        orderDetails.Metadata = options.Metadata;\n                        tradingService.Account.AddBuyOrder(orderDetails);\n                        tradingService.Account.Save();\n                        tradingService.LogOrder(orderDetails);\n\n                        decimal fees = tradingService.CalculateOrderFees(orderDetails);\n                        tradingPair = tradingService.Account.GetTradingPair(orderDetails.Pair, includeDust: true);\n                        loggingService.Info(\"{@Trade}\", orderDetails);\n                        loggingService.Info($\"Buy order result for {orderDetails.OriginalPair ?? tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). \" +\n                            $\"Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, \" +\n                            $\"Filled: {orderDetails.AmountFilled:0.########}, Cost: {orderDetails.Cost:0.00000000}, Fees: {fees:0.00000000}\");\n                        notificationService.Notify($\"Bought {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, \" +\n                            $\"Price: {orderDetails.AveragePrice:0.00000000}, Cost: {(orderDetails.Cost + fees):0.00000000}\");\n                    }\n\n                    tradingService.ReapplyTradingRules();\n                }\n                else\n                {\n                    loggingService.Info(message);\n                }\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error($\"Unable to place buy order for {options.Pair}\", ex);\n                notificationService.Notify($\"Unable to buy {options.Pair}: {ex.Message}\");\n            }\n            return orderDetails;\n        }\n\n        public IOrderDetails PlaceSellOrder(SellOptions options)\n        {\n            OrderDetails orderDetails = new OrderDetails();\n            tradingService.StopTrailingSell(options.Pair);\n            tradingService.StopTrailingBuy(options.Pair);\n\n            try\n            {\n                string normalizedPair = tradingService.NormalizePair(options.Pair);\n                ITradingPair tradingPair = tradingService.Account.GetTradingPair(normalizedPair, includeDust: true);\n                options.Price = tradingService.GetPrice(options.Pair, TradePriceType.Bid);\n                options.Amount = options.Amount ?? tradingPair?.Amount ?? 0;\n                options.Price = options.Price != 1 ? tradingService.Exchange.ClampOrderPrice(options.Pair, options.Price.Value) : 1; // 1 = USDT price\n                options.Amount = tradingService.Exchange.ClampOrderAmount(options.Pair, options.Amount.Value);\n\n                if (tradingService.CanSell(options, out string message))\n                {\n                    IPairConfig pairConfig = tradingService.GetPairConfig(normalizedPair);\n                    SellOrder sellOrder = new SellOrder\n                    {\n                        Type = pairConfig.SellType,\n                        Date = DateTimeOffset.Now,\n                        Pair = options.Pair,\n                        Price = options.Price.Value,\n                        Amount = options.Amount.Value\n                    };\n\n                    lock (tradingService.Account.SyncRoot)\n                    {\n                        tradingPair.SetCurrentValues(tradingService.GetPrice(normalizedPair), tradingService.Exchange.GetPriceSpread(normalizedPair));\n                        string sellPairName = normalizedPair != options.Pair ? options.Pair : tradingPair.FormattedName;\n                        loggingService.Info($\"Place sell order for {sellPairName}. \" +\n                            $\"Price: {sellOrder.Price:0.00000000}, Amount: {sellOrder.Amount:0.########}, Margin: {tradingPair.CurrentMargin:0.00}\");\n\n                        if (!tradingService.Config.VirtualTrading)\n                        {\n                            orderDetails = tradingService.Exchange.PlaceOrder(sellOrder) as OrderDetails;\n                        }\n                        else\n                        {\n                            string pairMarket = tradingService.Exchange.GetPairMarket(options.Pair);\n                            orderDetails = new OrderDetails\n                            {\n                                OrderId = DateTime.Now.ToFileTimeUtc().ToString(),\n                                Side = OrderSide.Sell,\n                                Result = OrderResult.Filled,\n                                Date = sellOrder.Date,\n                                Pair = sellOrder.Pair,\n                                Amount = sellOrder.Amount,\n                                AmountFilled = sellOrder.Amount,\n                                Price = sellOrder.Price,\n                                AveragePrice = sellOrder.Price,\n                                Fees = sellOrder.Amount * sellOrder.Price * tradingService.Config.VirtualTradingFees,\n                                FeesCurrency = pairMarket\n                            };\n                        }\n\n                        NormalizeOrder(orderDetails, TradePriceType.Bid);\n                        tradingPair.SetMetadata(tradingPair.Metadata.MergeWith(options.Metadata));\n                        orderDetails.Metadata = tradingPair.Metadata;\n                        var tradeResult = tradingService.Account.AddSellOrder(orderDetails) as TradeResult;\n                        tradeResult.IsSwap = options.Swap;\n                        tradeResult.IsArbitrage = options.Arbitrage;\n                        tradingService.Account.Save();\n                        tradingService.LogOrder(orderDetails);\n\n                        decimal fees = tradingService.CalculateOrderFees(orderDetails);\n                        decimal margin = (tradeResult.Profit / (tradeResult.Cost + (tradeResult.Metadata.AdditionalCosts ?? 0)) * 100);\n                        string swapPair = options.Metadata.SwapPair != null ? $\", Swap Pair: {options.Metadata.SwapPair}\" : \"\";\n                        string arbitrage = options.Metadata.Arbitrage != null ? $\", Arbitrage: {options.Metadata.Arbitrage} ({options.Metadata.ArbitragePercentage:0.00})\" : \"\";\n                        loggingService.Info(\"{@Trade}\", orderDetails);\n                        loggingService.Info(\"{@Trade}\", tradeResult);\n                        loggingService.Info($\"Sell order result for {orderDetails.OriginalPair ?? tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). \" +\n                            $\"Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, Filled: {orderDetails.AmountFilled:0.########}, \" +\n                            $\"Cost: {orderDetails.Cost:0.00000000}, Fees: {fees:0.00000000}, Margin: {margin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}{arbitrage}\");\n                        notificationService.Notify($\"Sold {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, \" +\n                            $\"Price: {orderDetails.AveragePrice:0.00000000}, Margin: {margin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}{arbitrage}\");\n                    }\n\n                    tradingService.ReapplyTradingRules();\n                }\n                else\n                {\n                    loggingService.Info(message);\n                }\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error($\"Unable to place sell order for {options.Pair}\", ex);\n                notificationService.Notify($\"Unable to sell {options.Pair}: {ex.Message}\");\n            }\n            return orderDetails;\n        }\n\n        private void NormalizeOrder(OrderDetails orderDetails, TradePriceType priceType)\n        {\n            if (!tradingService.IsNormalizedPair(orderDetails.Pair))\n            {\n                string pairMarket = tradingService.Exchange.GetPairMarket(orderDetails.Pair);\n\n                if (pairMarket != Constants.Markets.USDT || orderDetails.Price != 1)\n                {\n                    orderDetails.Price = tradingService.Exchange.ConvertPrice(orderDetails.Pair, orderDetails.Price, tradingService.Config.Market, priceType);\n                    orderDetails.AveragePrice = tradingService.Exchange.ConvertPrice(orderDetails.Pair, orderDetails.AveragePrice, tradingService.Config.Market, priceType);\n                }\n                else if (pairMarket == Constants.Markets.USDT && orderDetails.Pair.StartsWith(tradingService.Config.Market))\n                {\n                    orderDetails.Amount = orderDetails.Amount / tradingService.GetPrice(orderDetails.Pair, priceType, normalize: false);\n                    orderDetails.AmountFilled = orderDetails.AmountFilled / tradingService.GetPrice(orderDetails.Pair, priceType, normalize: false);\n                }\n\n                if (orderDetails.FeesCurrency == tradingService.Exchange.GetPairMarket(orderDetails.Pair))\n                {\n                    orderDetails.Fees = tradingService.Exchange.ConvertPrice(orderDetails.Pair, orderDetails.Fees, tradingService.Config.Market, priceType);\n                    orderDetails.FeesCurrency = tradingService.Config.Market;\n                }\n                orderDetails.OriginalPair = orderDetails.Pair;\n                if (!orderDetails.Pair.StartsWith(tradingService.Config.Market))\n                {\n                    orderDetails.Pair = tradingService.Exchange.ChangeMarket(orderDetails.Pair, tradingService.Config.Market);\n                }\n\n                orderDetails.IsNormalized = true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/Services/TradingService.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Exchange.Base;\nusing IntelliTrader.Signals.Base;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\n\nnamespace IntelliTrader.Trading\n{\n    internal class TradingService : ConfigrableServiceBase<TradingConfig>, ITradingService\n    {\n        private const int MIN_INTERVAL_BETWEEN_BUY_AND_SELL = 7000;\n        private const decimal DEFAULT_ARBITRAGE_BUY_MULTIPLIER = 0.99M;\n        private const decimal DEFAULT_ARBITRAGE_SELL_MULTIPLIER = 0.99M;\n\n        public override string ServiceName => Constants.ServiceNames.TradingService;\n\n        ITradingConfig ITradingService.Config => Config;\n        public IModuleRules Rules { get; private set; }\n        public TradingRulesConfig RulesConfig { get; private set; }\n\n        public IExchangeService Exchange { get; private set; }\n        public ITradingAccount Account { get; private set; }\n        public ConcurrentStack<IOrderDetails> OrderHistory { get; private set; } = new ConcurrentStack<IOrderDetails>();\n        public bool IsTradingSuspended { get; private set; }\n\n        private readonly ILoggingService loggingService;\n        private readonly INotificationService notificationService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITasksService tasksService;\n        private IOrderingService orderingService;\n        private IRulesService rulesService;\n        private ISignalsService signalsService;\n\n        private TradingTimedTask tradingTimedTask;\n        private TradingRulesTimedTask tradingRulesTimedTask;\n        private AccountRefreshTimedTask accountRefreshTimedTask;\n\n        private bool tradingForcefullySuspended;\n        private object syncRoot = new object();\n\n        public TradingService(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ITasksService tasksService)\n        {\n            this.loggingService = loggingService;\n            this.notificationService = notificationService;\n            this.healthCheckService = healthCheckService;\n            this.tasksService = tasksService;\n\n            var isBacktesting = Application.Resolve<IBacktestingService>().Config.Enabled && Application.Resolve<IBacktestingService>().Config.Replay;\n            if (isBacktesting)\n            {\n                this.Exchange = Application.ResolveOptionalNamed<IExchangeService>(Constants.ServiceNames.BacktestingExchangeService);\n            }\n            else\n            {\n                this.Exchange = Application.ResolveOptionalNamed<IExchangeService>(Config.Exchange);\n            }\n\n            if (this.Exchange == null)\n            {\n                throw new Exception($\"Unsupported exchange: {Config.Exchange}\");\n            }\n        }\n\n        public void Start()\n        {\n            loggingService.Info($\"Start Trading service (Virtual: {Config.VirtualTrading})...\");\n\n            IsTradingSuspended = true;\n\n            orderingService = Application.Resolve<IOrderingService>();\n            rulesService = Application.Resolve<IRulesService>();\n            OnTradingRulesChanged();\n            rulesService.RegisterRulesChangeCallback(OnTradingRulesChanged);\n            Exchange.Start(Config.VirtualTrading);\n            signalsService = Application.Resolve<ISignalsService>();\n\n            if (!Config.VirtualTrading)\n            {\n                Account = new ExchangeAccount(loggingService, notificationService, healthCheckService, signalsService, this);\n            }\n            else\n            {\n                Account = new VirtualAccount(loggingService, notificationService, healthCheckService, signalsService, this);\n            }\n\n            accountRefreshTimedTask = tasksService.AddTask(\n                name: nameof(AccountRefreshTimedTask),\n                task: new AccountRefreshTimedTask(loggingService, healthCheckService, this),\n                interval: Config.AccountRefreshInterval * 1000 / Application.Speed,\n                startDelay: Constants.TaskDelays.ZeroDelay,\n                startTask: false,\n                runNow: true,\n                skipIteration: 0);\n\n            if (signalsService.Config.Enabled)\n            {\n                signalsService.Start();\n            }\n\n            tradingTimedTask = tasksService.AddTask(\n                name: nameof(TradingTimedTask),\n                task: new TradingTimedTask(loggingService, notificationService, healthCheckService, signalsService, orderingService, this),\n                interval: Config.TradingCheckInterval * 1000 / Application.Speed,\n                startDelay: Constants.TaskDelays.NormalDelay,\n                startTask: false,\n                runNow: false,\n                skipIteration: 0);\n\n            tradingRulesTimedTask = tasksService.AddTask(\n                name: nameof(TradingRulesTimedTask),\n                task: new TradingRulesTimedTask(loggingService, notificationService, healthCheckService, rulesService, signalsService, this),\n                interval: RulesConfig.CheckInterval * 1000 / Application.Speed,\n                startDelay: Constants.TaskDelays.MidDelay,\n                startTask: false,\n                runNow: false,\n                skipIteration: 0);\n\n            IsTradingSuspended = false;\n\n            loggingService.Info(\"Trading service started\");\n        }\n\n        public void Stop()\n        {\n            loggingService.Info(\"Stop Trading service...\");\n\n            Exchange.Stop();\n\n            if (signalsService.Config.Enabled)\n            {\n                signalsService.Stop();\n            }\n\n            tasksService.RemoveTask(nameof(TradingTimedTask), stopTask: true);\n            tasksService.RemoveTask(nameof(TradingRulesTimedTask), stopTask: true);\n            tasksService.RemoveTask(nameof(AccountRefreshTimedTask), stopTask: true);\n\n            Account.Dispose();\n\n            rulesService.UnregisterRulesChangeCallback(OnTradingRulesChanged);\n\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingRulesProcessed);\n            healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingPairsProcessed);\n\n            loggingService.Info(\"Trading service stopped\");\n        }\n\n        public void ResumeTrading(bool forced)\n        {\n            if (IsTradingSuspended && (!tradingForcefullySuspended || forced))\n            {\n                loggingService.Info(\"Trading started\");\n                IsTradingSuspended = false;\n\n                tradingTimedTask.Start();\n                tradingRulesTimedTask.Start();\n                tradingRulesTimedTask.RunNow();\n            }\n        }\n\n        public void SuspendTrading(bool forced)\n        {\n            if (!IsTradingSuspended)\n            {\n                loggingService.Info(\"Trading suspended\");\n                IsTradingSuspended = true;\n                tradingForcefullySuspended = forced;\n\n                tradingRulesTimedTask.Stop();\n                tradingTimedTask.Stop();\n                tradingTimedTask.StopTrailing();\n            }\n        }\n\n        public IPairConfig GetPairConfig(string pair)\n        {\n            return tradingRulesTimedTask.GetPairConfig(pair);\n        }\n\n        public void ReapplyTradingRules()\n        {\n            tradingRulesTimedTask.RunNow();\n        }\n\n        public void Buy(BuyOptions options)\n        {\n            lock (syncRoot)\n            {\n                PauseTasks();\n                try\n                {\n                    IRule rule = signalsService.Rules.Entries.FirstOrDefault(r => r.Name == options.Metadata.SignalRule);\n                    RuleAction ruleAction = rule?.Action ?? RuleAction.Default;\n                    IPairConfig pairConfig = GetPairConfig(options.Pair);\n\n                    bool arbitragePair = pairConfig.ArbitrageEnabled && pairConfig.ArbitrageSignalRules.Contains(options.Metadata.SignalRule);\n                    if (arbitragePair)\n                    {\n                        Arbitrage arbitrage = Exchange.GetArbitrage(options.Pair, Config.Market, pairConfig.ArbitrageMarkets, pairConfig.ArbitrageType);\n                        if (arbitrage.IsAssigned)\n                        {\n                            Arbitrage(new ArbitrageOptions(options.Pair, arbitrage, options.Metadata));\n                        }\n                    }\n                    else\n                    {\n                        ITradingPair swappedPair = Account.GetTradingPairs().OrderBy(p => p.CurrentMargin).FirstOrDefault(tradingPair =>\n                        {\n                            IPairConfig tradingPairConfig = GetPairConfig(tradingPair.Pair);\n                            return tradingPairConfig.SellEnabled && tradingPairConfig.SwapEnabled && tradingPairConfig.SwapSignalRules != null &&\n                                   tradingPairConfig.SwapSignalRules.Contains(options.Metadata.SignalRule) &&\n                                   tradingPairConfig.SwapTimeout < (DateTimeOffset.Now - tradingPair.OrderDates.DefaultIfEmpty().Max()).TotalSeconds;\n                        });\n\n                        if (swappedPair != null)\n                        {\n                            Swap(new SwapOptions(swappedPair.Pair, options.Pair, options.Metadata));\n                        }\n                        else if (ruleAction == RuleAction.Default)\n                        {\n                            if (CanBuy(options, out string message))\n                            {\n                                tradingTimedTask.InitiateBuy(options);\n                            }\n                            else\n                            {\n                                loggingService.Debug(message);\n                            }\n                        }\n                    }\n                }\n                finally\n                {\n                    ContinueTasks();\n                }\n            }\n        }\n\n        public void Sell(SellOptions options)\n        {\n            lock (syncRoot)\n            {\n                PauseTasks();\n                try\n                {\n                    if (CanSell(options, out string message))\n                    {\n                        tradingTimedTask.InitiateSell(options);\n                    }\n                    else\n                    {\n                        loggingService.Debug(message);\n                    }\n                }\n                finally\n                {\n                    ContinueTasks();\n                }\n            }\n        }\n\n        public void Swap(SwapOptions options)\n        {\n            lock (syncRoot)\n            {\n                PauseTasks();\n                try\n                {\n                    if (CanSwap(options, out string message))\n                    {\n                        ITradingPair oldTradingPair = Account.GetTradingPair(options.OldPair);\n                        var sellOptions = new SellOptions(options.OldPair)\n                        {\n                            Swap = true,\n                            ManualOrder = options.ManualOrder,\n                            Metadata = new OrderMetadata { SwapPair = options.NewPair }\n                        };\n\n                        if (CanSell(sellOptions, out message))\n                        {\n                            decimal currentMargin = oldTradingPair.CurrentMargin;\n                            decimal additionalCosts = oldTradingPair.Cost - oldTradingPair.CurrentCost + (oldTradingPair.Metadata.AdditionalCosts ?? 0);\n                            int additionalDCALevels = oldTradingPair.DCALevel;\n\n                            IOrderDetails sellOrderDetails = orderingService.PlaceSellOrder(sellOptions);\n                            if (!Account.HasTradingPair(options.OldPair))\n                            {\n                                var buyOptions = new BuyOptions(options.NewPair)\n                                {\n                                    Swap = true,\n                                    ManualOrder = options.ManualOrder,\n                                    MaxCost = sellOrderDetails.Cost,\n                                    Metadata = options.Metadata\n                                };\n                                buyOptions.Metadata.LastBuyMargin = currentMargin;\n                                buyOptions.Metadata.SwapPair = options.OldPair;\n                                buyOptions.Metadata.AdditionalDCALevels = additionalDCALevels;\n                                buyOptions.Metadata.AdditionalCosts = additionalCosts;\n                                IOrderDetails buyOrderDetails = orderingService.PlaceBuyOrder(buyOptions);\n\n                                var newTradingPair = Account.GetTradingPair(options.NewPair) as TradingPair;\n                                if (newTradingPair != null)\n                                {\n                                    newTradingPair.Metadata.AdditionalCosts += CalculateOrderFees(sellOrderDetails);\n                                    loggingService.Info($\"Swap {oldTradingPair.FormattedName} for {newTradingPair.FormattedName}. \" +\n                                        $\"Old margin: {oldTradingPair.CurrentMargin:0.00}, new margin: {newTradingPair.CurrentMargin:0.00}\");\n                                }\n                                else\n                                {\n                                    loggingService.Info($\"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to buy {options.NewPair}\");\n                                    notificationService.Notify($\"Unable to swap {options.OldPair} for {options.NewPair}: Failed to buy {options.NewPair}\");\n                                }\n                            }\n                            else\n                            {\n                                loggingService.Info($\"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to sell {options.OldPair}\");\n                            }\n                        }\n                        else\n                        {\n                            loggingService.Info($\"Unable to swap {options.OldPair} for {options.NewPair}: {message}\");\n                        }\n                    }\n                    else\n                    {\n                        loggingService.Info(message);\n                    }\n                }\n                finally\n                {\n                    ContinueTasks();\n                }\n            }\n        }\n\n        public void Arbitrage(ArbitrageOptions options)\n        {\n            lock (syncRoot)\n            {\n                PauseTasks();\n                try\n                {\n                    if (CanArbitrage(options, out string message))\n                    {\n                        if (CanBuy(new BuyOptions(options.Pair) { Amount = 1 }, out message))\n                        {\n                            options.Metadata.Arbitrage = $\"{options.Arbitrage.Market}-\" + options.Arbitrage.Type ?? \"All\";\n                            options.Metadata.ArbitragePercentage = options.Arbitrage.Percentage;\n                            loggingService.Info($\"{options.Arbitrage.Type} arbitrage {options.Pair} on {options.Arbitrage.Market}. Percentage: {options.Arbitrage.Percentage:0.00}\");\n\n                            if (options.Arbitrage.Type == ArbitrageType.Direct)\n                            {\n                                ArbitrageDirect(options);\n                            }\n                            else if (options.Arbitrage.Type == ArbitrageType.Reverse)\n                            {\n                                ArbitrageReverse(options);\n                            }\n                        }\n                        else\n                        {\n                            loggingService.Info($\"Unable to arbitrage {options.Pair}: {message}\");\n                        }\n                    }\n                    else\n                    {\n                        loggingService.Info(message);\n                    }\n                }\n                finally\n                {\n                    ContinueTasks();\n                }\n            }\n        }\n\n        private void ArbitrageDirect(ArbitrageOptions options)\n        {\n            string arbitragePair = options.Pair;\n            ITradingPair existingArbitragePair = Account.GetTradingPair(arbitragePair);\n            IPairConfig pairConfig = GetPairConfig(options.Pair);\n            bool useExistingArbitragePair = (existingArbitragePair != null && existingArbitragePair.CurrentCost > pairConfig.BuyMaxCost &&\n                                            existingArbitragePair.AveragePrice <= existingArbitragePair.CurrentPrice);\n\n            var buyArbitragePairOptions = new BuyOptions(arbitragePair)\n            {\n                Arbitrage = true,\n                MaxCost = pairConfig.BuyMaxCost,\n                ManualOrder = options.ManualOrder,\n                IgnoreBalance = useExistingArbitragePair,\n                Metadata = options.Metadata\n            };\n\n            if (CanBuy(buyArbitragePairOptions, out string message))\n            {\n                IOrderDetails buyArbitragePairOrderDetails = null;\n                if (useExistingArbitragePair)\n                {\n                    buyArbitragePairOrderDetails = Account.AddBlankOrder(buyArbitragePairOptions.Pair,\n                        buyArbitragePairOptions.MaxCost.Value / GetPrice(buyArbitragePairOptions.Pair, TradePriceType.Ask),\n                        includeFees: false);\n                    loggingService.Info($\"Use existing arbitrage pair for arbitrage: {arbitragePair}. \" +\n                        $\"Average price: {existingArbitragePair.AveragePrice}, Current price: {existingArbitragePair.CurrentPrice}\");\n                }\n                else\n                {\n                    buyArbitragePairOrderDetails = orderingService.PlaceBuyOrder(buyArbitragePairOptions);\n                }\n\n                if (buyArbitragePairOrderDetails.Result == OrderResult.Filled)\n                {\n                    decimal buyArbitragePairFees = CalculateOrderFees(buyArbitragePairOrderDetails);\n                    string flippedArbitragePair = Exchange.ChangeMarket(arbitragePair, options.Arbitrage.Market.ToString());\n                    var sellArbitragePairOptions = new SellOptions(flippedArbitragePair)\n                    {\n                        Arbitrage = true,\n                        Amount = buyArbitragePairOrderDetails.AmountFilled,\n                        ManualOrder = options.ManualOrder,\n                        Metadata = options.Metadata.MergeWith(new OrderMetadata\n                        {\n                            IsTransitional = true\n                        })\n                    };\n\n                    IOrderDetails sellArbitragePairOrderDetails = orderingService.PlaceSellOrder(sellArbitragePairOptions);\n                    if (sellArbitragePairOrderDetails.Result == OrderResult.Filled)\n                    {\n                        decimal sellArbitragePairMultiplier = pairConfig.ArbitrageSellMultiplier ?? DEFAULT_ARBITRAGE_SELL_MULTIPLIER;\n                        decimal sellArbitragePairFees = CalculateOrderFees(sellArbitragePairOrderDetails);\n                        options.Metadata.FeesNonDeductible = buyArbitragePairFees  * sellArbitragePairMultiplier;\n                        decimal sellMarketPairAmount = sellArbitragePairOrderDetails.AmountFilled * GetPrice(flippedArbitragePair, TradePriceType.Bid, normalize: false) * sellArbitragePairMultiplier;\n                        string marketPair = Exchange.GetArbitrageMarketPair(options.Arbitrage.Market);\n\n                        var sellMarketPairOptions = new SellOptions(marketPair)\n                        {\n                            Arbitrage = true,\n                            Amount = sellMarketPairAmount,\n                            ManualOrder = options.ManualOrder,\n                            Metadata = options.Metadata.MergeWith(new OrderMetadata\n                            {\n                                IsTransitional = false,\n                                OriginalPair = arbitragePair\n                            })\n                        };\n\n                        existingArbitragePair = Account.GetTradingPair(marketPair);\n                        existingArbitragePair.OverrideCost((buyArbitragePairOrderDetails.Cost + sellArbitragePairFees * 2) * sellArbitragePairMultiplier);\n                        IOrderDetails sellMarketPairOrderDetails = orderingService.PlaceSellOrder(sellMarketPairOptions);\n                        existingArbitragePair.OverrideCost(null);\n\n                        if (sellMarketPairOrderDetails.Result == OrderResult.Filled)\n                        {\n                            loggingService.Info($\"{pairConfig.ArbitrageType} arbitrage successful: {arbitragePair} -> {flippedArbitragePair} -> {marketPair}\");\n                        }\n                        else\n                        {\n                            loggingService.Info($\"Unable to arbitrage {options.Pair}. Reason: failed to sell market pair {arbitragePair}\");\n                            notificationService.Notify($\"Unable to arbitrage {options.Pair}: Failed to sell market pair {arbitragePair}\");\n                        }\n                    }\n                    else\n                    {\n                        loggingService.Info($\"Unable to arbitrage {options.Pair}. Reason: failed to sell arbitrage pair {flippedArbitragePair}\");\n                        notificationService.Notify($\"Unable to arbitrage {options.Pair}: Failed to sell arbitrage pair {flippedArbitragePair}\");\n                    }\n                }\n                else\n                {\n                    loggingService.Info($\"Unable to arbitrage {options.Pair}. Reason: failed to buy arbitrage pair {arbitragePair}\");\n                }\n            }\n            else\n            {\n                loggingService.Info($\"Unable to arbitrage {options.Pair}: {message}\");\n            }\n        }\n\n        private void ArbitrageReverse(ArbitrageOptions options)\n        {\n            string marketPair = Exchange.GetArbitrageMarketPair(options.Arbitrage.Market);\n            ITradingPair existingMarketPair = Account.GetTradingPair(marketPair);\n            IPairConfig pairConfig = GetPairConfig(options.Pair);\n            bool useExistingMarketPair = (existingMarketPair != null && existingMarketPair.CurrentCost > pairConfig.BuyMaxCost &&\n                                         existingMarketPair.AveragePrice <= existingMarketPair.CurrentPrice);\n\n            var buyMarketPairOptions = new BuyOptions(marketPair)\n            {\n                Arbitrage = true,\n                MaxCost = pairConfig.BuyMaxCost,\n                ManualOrder = options.ManualOrder,\n                IgnoreBalance = useExistingMarketPair,\n                Metadata = options.Metadata\n            };\n\n            if (CanBuy(buyMarketPairOptions, out string message))\n            {\n                IOrderDetails buyMarketPairOrderDetails = null;\n                if (useExistingMarketPair)\n                {\n                    buyMarketPairOrderDetails = Account.AddBlankOrder(buyMarketPairOptions.Pair,\n                        buyMarketPairOptions.MaxCost.Value / GetPrice(buyMarketPairOptions.Pair, TradePriceType.Ask),\n                        includeFees: false);\n                    loggingService.Info($\"Use existing market pair for arbitrage: {marketPair}. \" +\n                        $\"Average price: {existingMarketPair.AveragePrice}, Current price: {existingMarketPair.CurrentPrice}\");\n                }\n                else\n                {\n                    buyMarketPairOrderDetails = orderingService.PlaceBuyOrder(buyMarketPairOptions);\n                }\n\n                if (buyMarketPairOrderDetails.Result == OrderResult.Filled)\n                {\n                    decimal buyArbitragePairMultiplier = pairConfig.ArbitrageBuyMultiplier ?? DEFAULT_ARBITRAGE_BUY_MULTIPLIER;\n                    decimal buyMarketPairFees = CalculateOrderFees(buyMarketPairOrderDetails);\n                    string arbitragePair = Exchange.ChangeMarket(options.Pair, options.Arbitrage.Market.ToString());\n                    decimal buyArbitragePairAmount = options.Arbitrage.Market == ArbitrageMarket.USDT ?\n                        buyMarketPairOrderDetails.AmountFilled * GetPrice(buyMarketPairOrderDetails.Pair, TradePriceType.Ask, normalize: false) / GetPrice(arbitragePair, TradePriceType.Ask) :\n                        buyMarketPairOrderDetails.AmountFilled / GetPrice(arbitragePair, TradePriceType.Ask);\n\n                    var buyArbitragePairOptions = new BuyOptions(arbitragePair)\n                    {\n                        Arbitrage = true,\n                        ManualOrder = options.ManualOrder,\n                        Amount = buyArbitragePairAmount * buyArbitragePairMultiplier,\n                        Metadata = options.Metadata\n                    };\n\n                    IOrderDetails buyArbitragePairOrderDetails = orderingService.PlaceBuyOrder(buyArbitragePairOptions);\n                    if (buyArbitragePairOrderDetails.Result == OrderResult.Filled)\n                    {\n                        decimal buyArbitragePairFees = CalculateOrderFees(buyArbitragePairOrderDetails);\n                        options.Metadata.FeesNonDeductible = buyMarketPairFees * buyArbitragePairMultiplier;\n                        var sellArbitragePairOptions = new SellOptions(buyArbitragePairOrderDetails.Pair)\n                        {\n                            Arbitrage = true,\n                            Amount = buyArbitragePairOrderDetails.AmountFilled,\n                            ManualOrder = options.ManualOrder,\n                            Metadata = options.Metadata\n                        };\n\n                        TradingPair existingArbitragePair = Account.GetTradingPair(buyArbitragePairOrderDetails.Pair) as TradingPair;\n                        existingArbitragePair.OverrideCost(buyArbitragePairOrderDetails.Cost + buyArbitragePairFees * 2);\n                        IOrderDetails sellArbitragePairOrderDetails = orderingService.PlaceSellOrder(sellArbitragePairOptions);\n                        existingArbitragePair.OverrideCost(null);\n\n                        if (sellArbitragePairOrderDetails.Result == OrderResult.Filled)\n                        {\n                            loggingService.Info($\"{pairConfig.ArbitrageType} arbitrage successful: {marketPair} -> {arbitragePair} -> {existingArbitragePair.Pair}\");\n                        }\n                        else\n                        {\n                            loggingService.Info($\"Unable to arbitrage {options.Pair}. Reason: failed to sell arbitrage pair {arbitragePair}\");\n                            notificationService.Notify($\"Unable to arbitrage {options.Pair}: Failed to sell arbitrage pair {arbitragePair}\");\n                        }\n                    }\n                    else\n                    {\n                        loggingService.Info($\"Unable to arbitrage {options.Pair}. Reason: failed to buy arbitrage pair {arbitragePair}\");\n                        notificationService.Notify($\"Unable to arbitrage {options.Pair}: Failed to buy arbitrage pair {arbitragePair}\");\n                    }\n                }\n                else\n                {\n                    loggingService.Info($\"Unable to arbitrage {options.Pair}. Reason: failed to buy market pair {marketPair}\");\n                }\n            }\n            else\n            {\n                loggingService.Info($\"Unable to arbitrage {options.Pair}: {message}\");\n            }\n        }\n\n        public bool CanBuy(BuyOptions options, out string message)\n        {\n            IPairConfig pairConfig = GetPairConfig(options.Pair);\n\n            if (!options.ManualOrder && !options.Swap && IsTradingSuspended)\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: trading suspended\";\n                return false;\n            }\n            else if (!options.ManualOrder && !options.Swap && !pairConfig.BuyEnabled)\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: buying not enabled\";\n                return false;\n            }\n            else if (!options.ManualOrder && Config.ExcludedPairs.Contains(options.Pair))\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: exluded pair\";\n                return false;\n            }\n            else if (!options.ManualOrder && !options.Arbitrage && !options.IgnoreExisting && Account.HasTradingPair(options.Pair))\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: pair already exists\";\n                return false;\n            }\n            else if (!options.ManualOrder && !options.Swap && !options.Arbitrage && pairConfig.MaxPairs != 0 && Account.GetTradingPairs().Count() >= pairConfig.MaxPairs && !Account.HasTradingPair(options.Pair))\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: maximum pairs reached\";\n                return false;\n            }\n            else if (!options.ManualOrder && !options.Swap && !options.IgnoreBalance && pairConfig.BuyMinBalance != 0 && (Account.GetBalance() - options.MaxCost) < pairConfig.BuyMinBalance && Exchange.GetPairMarket(options.Pair) == Config.Market)\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: minimum balance reached\";\n                return false;\n            }\n            else if (options.Price != null && options.Price <= 0)\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: invalid price\";\n                return false;\n            }\n            else if (options.Amount != null && options.Amount <= 0)\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: invalid amount\";\n                return false;\n            }\n            else if (!options.IgnoreBalance && Account.GetBalance() < options.MaxCost && Exchange.GetPairMarket(options.Pair) == Config.Market)\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: not enough balance\";\n                return false;\n            }\n            else if (options.Amount == null && options.MaxCost == null || options.Amount != null && options.MaxCost != null)\n            {\n                message = $\"Cancel buy request for {options.Pair}. Reason: either max cost or amount needs to be specified (not both)\";\n            }\n            else if (!options.ManualOrder && !options.Swap && !options.Arbitrage && pairConfig.BuySamePairTimeout > 0 &&\n                OrderHistory.Any(h => h.Side == OrderSide.Buy && (h.Pair == options.Pair || h.Pair == h.OriginalPair)) &&\n                (DateTimeOffset.Now - OrderHistory.Where(h => (h.Pair == options.Pair || h.Pair == h.OriginalPair)).Max(h => h.Date)).TotalSeconds < pairConfig.BuySamePairTimeout)\n            {\n                var elapsedSeconds = (DateTimeOffset.Now - OrderHistory.Where(h => (h.Pair == options.Pair || h.Pair == h.OriginalPair)).Max(h => h.Date)).TotalSeconds;\n                message = $\"Cancel buy request for {options.Pair}. Reason: buy same pair timeout (elapsed: {elapsedSeconds:0.#}, timeout: {pairConfig.BuySamePairTimeout:0.#})\";\n                return false;\n            }\n\n            message = null;\n            return true;\n        }\n\n        public bool CanSell(SellOptions options, out string message)\n        {\n            IPairConfig pairConfig = GetPairConfig(options.Pair);\n\n            if (!options.ManualOrder && !options.Arbitrage && IsTradingSuspended)\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: trading suspended\";\n                return false;\n            }\n            else if (!options.ManualOrder && !options.Arbitrage && !pairConfig.SellEnabled)\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: selling not enabled\";\n                return false;\n            }\n            else if (!options.ManualOrder && !options.Arbitrage && Config.ExcludedPairs.Contains(options.Pair))\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: excluded pair\";\n                return false;\n            }\n            else if (!Account.HasTradingPair(options.Pair, includeDust: true) && !Account.HasTradingPair(NormalizePair(options.Pair), includeDust: true))\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: pair does not exist\";\n                return false;\n            }\n            else if (options.Price != null && options.Price <= 0)\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: invalid price\";\n                return false;\n            }\n            else if (options.Amount != null && options.Amount <= 0)\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: invalid amount\";\n                return false;\n            }\n            else if (options.Amount != null && options.Price != null && (options.Amount * options.Price) < Config.MinCost)\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: dust\";\n                return false;\n            }\n            else if (!options.ManualOrder && !options.Arbitrage && (DateTimeOffset.Now - Account.GetTradingPair(options.Pair, includeDust: true).OrderDates.DefaultIfEmpty().Max()).\n                TotalMilliseconds < (MIN_INTERVAL_BETWEEN_BUY_AND_SELL / Application.Speed))\n            {\n                message = $\"Cancel sell request for {options.Pair}. Reason: pair just bought\";\n                return false;\n            }\n            message = null;\n            return true;\n        }\n\n        public bool CanSwap(SwapOptions options, out string message)\n        {\n            if (!Account.HasTradingPair(options.OldPair))\n            {\n                message = $\"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: pair does not exist\";\n                return false;\n            }\n            else if (Account.HasTradingPair(options.NewPair))\n            {\n                message = $\"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: pair already exists\";\n                return false;\n            }\n            else if (!options.ManualOrder && !GetPairConfig(options.OldPair).SellEnabled)\n            {\n                message = $\"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: selling not enabled\";\n                return false;\n            }\n            else if (!options.ManualOrder && !GetPairConfig(options.NewPair).BuyEnabled)\n            {\n                message = $\"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: buying not enabled\";\n                return false;\n            }\n            else if (Account.GetBalance() < Account.GetTradingPair(options.OldPair).CurrentCost * 0.01M)\n            {\n                message = $\"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: not enough balance\";\n                return false;\n            }\n            else if (!Exchange.GetMarketPairs(Config.Market).Contains(options.NewPair))\n            {\n                message = $\"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: {options.NewPair} is not a valid pair\";\n                return false;\n            }\n\n            message = null;\n            return true;\n        }\n\n        public bool CanArbitrage(ArbitrageOptions options, out string message)\n        {\n            if (Account.HasTradingPair(options.Pair))\n            {\n                message = $\"Cancel arbitrage request {options.Pair}. Reason: pair already exist\";\n                return false;\n            }\n            else if (!options.ManualOrder && !GetPairConfig(options.Pair).BuyEnabled)\n            {\n                message = $\"Cancel arbitrage request for {options.Pair}. Reason: buying not enabled\";\n                return false;\n            }\n            else if (!Exchange.GetMarketPairs(Config.Market).Contains(options.Pair))\n            {\n                message = $\"Cancel arbitrage request for {options.Pair}. Reason: {options.Pair} is not a valid pair\";\n                return false;\n            }\n\n            message = null;\n            return true;\n        }\n\n        public decimal GetPrice(string pair, TradePriceType? priceType = null, bool normalize = true)\n        {\n            if (normalize)\n            {\n                if (pair == Config.Market + Constants.Markets.USDT)\n                {\n                    return 1;\n                }\n            }\n            return Exchange.GetPrice(pair, priceType ?? Config.TradePriceType);\n        }\n\n        public decimal CalculateOrderFees(IOrderDetails order)\n        {\n            decimal orderFees = 0;\n            if (order.Fees != 0 && order.FeesCurrency != null)\n            {\n                if (order.FeesCurrency == Config.Market)\n                {\n                    orderFees = order.Fees;\n                }\n                else\n                {\n                    string feesPair = order.FeesCurrency + Config.Market;\n                    orderFees = GetPrice(feesPair, TradePriceType.Ask) * order.Fees;\n                }\n            }\n            return orderFees;\n        }\n\n        public bool IsNormalizedPair(string pair)\n        {\n            return Exchange.GetPairMarket(pair) == Config.Market;\n        }\n\n        public string NormalizePair(string pair)\n        {\n            return Exchange.ChangeMarket(pair, Config.Market);\n        }\n\n        public void LogOrder(IOrderDetails order)\n        {\n            OrderHistory.Push(order);\n        }\n\n        public List<string> GetTrailingBuys()\n        {\n            return tradingTimedTask.GetTrailingBuys();\n        }\n\n        public List<string> GetTrailingSells()\n        {\n            return tradingTimedTask.GetTrailingSells();\n        }\n\n        public void StopTrailingBuy(string pair)\n        {\n            tradingTimedTask.StopTrailingBuy(pair);\n        }\n\n        public void StopTrailingSell(string pair)\n        {\n            tradingTimedTask.StopTrailingSell(pair);\n        }\n\n        private void OnTradingRulesChanged()\n        {\n            Rules = rulesService.GetRules(ServiceName);\n            RulesConfig = Rules.GetConfiguration<TradingRulesConfig>();\n        }\n\n        protected override void PrepareConfig()\n        {\n            if (Config.ExcludedPairs == null)\n            {\n                Config.ExcludedPairs = new List<string>();\n            }\n\n            if (Config.DCALevels == null)\n            {\n                Config.DCALevels = new List<DCALevel>();\n            }\n        }\n\n        private void PauseTasks()\n        {\n            tasksService.GetTask(nameof(TradingTimedTask)).Pause();\n            tasksService.GetTask(nameof(TradingRulesTimedTask)).Pause();\n            tasksService.GetTask(nameof(SignalRulesTimedTask)).Pause();\n            tasksService.GetTask(\"BacktestingLoadSnapshotsTimedTask\")?.Pause();\n        }\n\n        private void ContinueTasks()\n        {\n            tasksService.GetTask(nameof(TradingTimedTask)).Continue();\n            tasksService.GetTask(nameof(TradingRulesTimedTask)).Continue();\n            tasksService.GetTask(nameof(SignalRulesTimedTask)).Continue();\n            tasksService.GetTask(\"BacktestingLoadSnapshotsTimedTask\")?.Continue();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/TimedTasks/AccountRefreshTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\n\nnamespace IntelliTrader.Trading\n{\n    public class AccountRefreshTimedTask : HighResolutionTimedTask\n    {\n        private readonly ILoggingService loggingService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ITradingService tradingService;\n\n        public AccountRefreshTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService)\n        {\n            this.loggingService = loggingService;\n            this.healthCheckService = healthCheckService;\n            this.tradingService = tradingService;\n        }\n\n        protected override void Run()\n        {\n            tradingService.Account.Refresh();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/TimedTasks/TradingRulesTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\n\nnamespace IntelliTrader.Trading\n{\n    public class TradingRulesTimedTask : HighResolutionTimedTask\n    {\n        private readonly ILoggingService loggingService;\n        private readonly INotificationService notificationService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly IRulesService rulesService;\n        private readonly ISignalsService signalsService;\n        private readonly TradingService tradingService;\n\n        private readonly ConcurrentDictionary<string, PairConfig> pairConfigs = new ConcurrentDictionary<string, PairConfig>();\n\n        public TradingRulesTimedTask(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, IRulesService rulesService, ISignalsService signalsService, ITradingService tradingService)\n        {\n            this.loggingService = loggingService;\n            this.notificationService = notificationService;\n            this.healthCheckService = healthCheckService;\n            this.rulesService = rulesService;\n            this.signalsService = signalsService;\n            this.tradingService = tradingService as TradingService;\n        }\n\n        protected override void Run()\n        {\n            ProcessAllRules();\n        }\n\n        public IPairConfig GetPairConfig(string pair)\n        {\n            PairConfig pairConfig;\n            if (!pairConfigs.TryGetValue(pair, out pairConfig))\n            {\n                ProcessAllRules();\n                if (!pairConfigs.TryGetValue(pair, out pairConfig))\n                {\n                    return CreatePairConfig(pair, tradingService.Config.Clone(), new PairConfig(), new List<IRule>());\n                }\n            }\n            return pairConfig;\n        }\n\n        public void ProcessAllRules()\n        {\n            IEnumerable<IRule> enabledRules = tradingService.Rules?.Entries?.Where(r => r.Enabled) ?? new List<IRule>();\n            List<string> allPairs = tradingService.Exchange.GetMarketPairs(tradingService.Config.Market).ToList();\n            double? globalRating = signalsService.GetGlobalRating();\n\n            foreach (string pair in allPairs)\n            {\n                IEnumerable<ISignal> signalsByPair = signalsService.GetSignalsByPair(pair);\n                if (signalsByPair != null)\n                {\n                    Dictionary<string, ISignal> signals = signalsByPair.ToDictionary(s => s.Name, s => s);\n                    ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair);\n                    TradingConfig modifiedTradingConfig = tradingService.Config.Clone() as TradingConfig;\n                    PairConfig modifiedPairConfig = new PairConfig();\n                    pairConfigs.TryGetValue(pair, out PairConfig oldPairConfig);\n                    var appliedRules = new List<IRule>();\n\n                    foreach (var rule in enabledRules)\n                    {\n                        if (rulesService.CheckConditions(rule.Conditions, signals, globalRating, pair, tradingPair))\n                        {\n                            var modifiers = rule.GetModifiers<TradingRuleModifiers>();\n                            if (modifiers != null)\n                            {\n                                // Base Trading Config\n                                modifiedTradingConfig.MaxPairs = modifiers.MaxPairs ?? modifiedTradingConfig.MaxPairs;\n                                modifiedTradingConfig.BuyEnabled = modifiers.BuyEnabled ?? modifiedTradingConfig.BuyEnabled;\n                                modifiedTradingConfig.BuyMaxCost = modifiers.BuyMaxCost ?? modifiedTradingConfig.BuyMaxCost;\n                                modifiedTradingConfig.BuyMultiplier = modifiers.BuyMultiplier ?? modifiedTradingConfig.BuyMultiplier;\n                                modifiedTradingConfig.BuyMinBalance = modifiers.BuyMinBalance ?? modifiedTradingConfig.BuyMinBalance;\n                                modifiedTradingConfig.BuySamePairTimeout = modifiers.BuySamePairTimeout ?? modifiedTradingConfig.BuySamePairTimeout;\n                                modifiedTradingConfig.BuyTrailing = modifiers.BuyTrailing ?? modifiedTradingConfig.BuyTrailing;\n                                modifiedTradingConfig.BuyTrailingStopMargin = modifiers.BuyTrailingStopMargin ?? modifiedTradingConfig.BuyTrailingStopMargin;\n                                modifiedTradingConfig.BuyTrailingStopAction = modifiers.BuyTrailingStopAction ?? modifiedTradingConfig.BuyTrailingStopAction;\n\n                                modifiedTradingConfig.BuyDCAEnabled = modifiers.BuyDCAEnabled ?? modifiedTradingConfig.BuyDCAEnabled;\n                                modifiedTradingConfig.BuyDCAMultiplier = modifiers.BuyDCAMultiplier ?? modifiedTradingConfig.BuyDCAMultiplier;\n                                modifiedTradingConfig.BuyDCAMinBalance = modifiers.BuyDCAMinBalance ?? modifiedTradingConfig.BuyDCAMinBalance;\n                                modifiedTradingConfig.BuyDCASamePairTimeout = modifiers.BuyDCASamePairTimeout ?? modifiedTradingConfig.BuyDCASamePairTimeout;\n                                modifiedTradingConfig.BuyDCATrailing = modifiers.BuyDCATrailing ?? modifiedTradingConfig.BuyDCATrailing;\n                                modifiedTradingConfig.BuyDCATrailingStopMargin = modifiers.BuyDCATrailingStopMargin ?? modifiedTradingConfig.BuyDCATrailingStopMargin;\n                                modifiedTradingConfig.BuyDCATrailingStopAction = modifiers.BuyDCATrailingStopAction ?? modifiedTradingConfig.BuyDCATrailingStopAction;\n\n                                modifiedTradingConfig.SellEnabled = modifiers.SellEnabled ?? modifiedTradingConfig.SellEnabled;\n                                modifiedTradingConfig.SellMargin = modifiers.SellMargin ?? modifiedTradingConfig.SellMargin;\n                                modifiedTradingConfig.SellTrailing = modifiers.SellTrailing ?? modifiedTradingConfig.SellTrailing;\n                                modifiedTradingConfig.SellTrailingStopMargin = modifiers.SellTrailingStopMargin ?? modifiedTradingConfig.SellTrailingStopMargin;\n                                modifiedTradingConfig.SellTrailingStopAction = modifiers.SellTrailingStopAction ?? modifiedTradingConfig.SellTrailingStopAction;\n                                modifiedTradingConfig.SellStopLossEnabled = modifiers.SellStopLossEnabled ?? modifiedTradingConfig.SellStopLossEnabled;\n                                modifiedTradingConfig.SellStopLossAfterDCA = modifiers.SellStopLossAfterDCA ?? modifiedTradingConfig.SellStopLossAfterDCA;\n                                modifiedTradingConfig.SellStopLossMinAge = modifiers.SellStopLossMinAge ?? modifiedTradingConfig.SellStopLossMinAge;\n                                modifiedTradingConfig.SellStopLossMargin = modifiers.SellStopLossMargin ?? modifiedTradingConfig.SellStopLossMargin;\n\n                                modifiedTradingConfig.SellDCAMargin = modifiers.SellDCAMargin ?? modifiedTradingConfig.SellDCAMargin;\n                                modifiedTradingConfig.SellDCATrailing = modifiers.SellDCATrailing ?? modifiedTradingConfig.SellDCATrailing;\n                                modifiedTradingConfig.SellDCATrailingStopMargin = modifiers.SellDCATrailingStopMargin ?? modifiedTradingConfig.SellDCATrailingStopMargin;\n                                modifiedTradingConfig.SellDCATrailingStopAction = modifiers.SellDCATrailingStopAction ?? modifiedTradingConfig.SellDCATrailingStopAction;\n\n                                modifiedTradingConfig.RepeatLastDCALevel = modifiers.RepeatLastDCALevel ?? modifiedTradingConfig.RepeatLastDCALevel;\n                                modifiedTradingConfig.DCALevels = modifiers.DCALevels ?? modifiedTradingConfig.DCALevels;\n\n                                // Base Pair Config\n                                modifiedPairConfig.SwapEnabled = modifiers.SwapEnabled ?? modifiedPairConfig.SwapEnabled;\n                                modifiedPairConfig.SwapSignalRules = modifiers.SwapSignalRules ?? modifiedPairConfig.SwapSignalRules;\n                                modifiedPairConfig.SwapTimeout = modifiers.SwapTimeout ?? modifiedPairConfig.SwapTimeout;\n\n                                modifiedPairConfig.ArbitrageEnabled = modifiers.ArbitrageEnabled ?? modifiedPairConfig.ArbitrageEnabled;\n                                modifiedPairConfig.ArbitrageMarkets = modifiers.ArbitrageMarkets ?? modifiedPairConfig.ArbitrageMarkets;\n                                modifiedPairConfig.ArbitrageType = modifiers.ArbitrageType ?? modifiedPairConfig.ArbitrageType;\n                                modifiedPairConfig.ArbitrageBuyMultiplier = modifiers.ArbitrageBuyMultiplier ?? modifiedPairConfig.ArbitrageBuyMultiplier;\n                                modifiedPairConfig.ArbitrageSellMultiplier = modifiers.ArbitrageSellMultiplier ?? modifiedPairConfig.ArbitrageSellMultiplier;\n                                modifiedPairConfig.ArbitrageSignalRules = modifiers.ArbitrageSignalRules ?? modifiedPairConfig.ArbitrageSignalRules;\n\n                                if (oldPairConfig != null && !oldPairConfig.ArbitrageEnabled && modifiedPairConfig.ArbitrageEnabled)\n                                {\n                                    signalsService.ProcessPair(pair, signals);\n                                }\n                            }\n\n                            appliedRules.Add(rule);\n\n                            if (tradingService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch)\n                            {\n                                break;\n                            }\n                        }\n                    }\n\n                    pairConfigs[pair] = CreatePairConfig(pair, modifiedTradingConfig, modifiedPairConfig, appliedRules);\n                }\n            }\n\n            healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TradingRulesProcessed, $\"Rules: {enabledRules.Count()}, Pairs: {allPairs.Count}\");\n        }\n\n        private PairConfig CreatePairConfig(string pair, ITradingConfig modifiedTradingConfig, IPairConfig modifiedPairConfig, IEnumerable<IRule> appliedRules)\n        {\n            ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair);\n            DCALevel currentDCALevel = GetCurrentDCALevel(tradingPair, modifiedTradingConfig.DCALevels);\n            DCALevel nextDCALevel = GetNextDCALevel(tradingPair, modifiedTradingConfig.DCALevels, modifiedTradingConfig.RepeatLastDCALevel);\n\n            return new PairConfig\n            {\n                Rules = appliedRules.Select(r => r.Name),\n\n                MaxPairs = modifiedTradingConfig.MaxPairs,\n\n                BuyEnabled = tradingPair == null ? modifiedTradingConfig.BuyEnabled : modifiedTradingConfig.BuyDCAEnabled,\n                BuyType = modifiedTradingConfig.BuyType,\n                BuyMaxCost = modifiedTradingConfig.BuyMaxCost,\n                BuyMultiplier = tradingPair == null ? (modifiedTradingConfig.BuyMultiplier != 0 ? modifiedTradingConfig.BuyMultiplier : 1) : nextDCALevel?.BuyMultiplier ?? modifiedTradingConfig.BuyDCAMultiplier,\n                BuyMinBalance = tradingPair == null ? modifiedTradingConfig.BuyMinBalance : modifiedTradingConfig.BuyDCAMinBalance,\n                BuySamePairTimeout = (tradingPair == null ? modifiedTradingConfig.BuySamePairTimeout : nextDCALevel?.BuySamePairTimeout ?? modifiedTradingConfig.BuyDCASamePairTimeout) / Application.Speed,\n                BuyTrailing = tradingPair == null ? modifiedTradingConfig.BuyTrailing : nextDCALevel?.BuyTrailing ?? modifiedTradingConfig.BuyDCATrailing,\n                BuyTrailingStopMargin = tradingPair == null ? modifiedTradingConfig.BuyTrailingStopMargin : nextDCALevel?.BuyTrailingStopMargin ?? modifiedTradingConfig.BuyDCATrailingStopMargin,\n                BuyTrailingStopAction = tradingPair == null ? modifiedTradingConfig.BuyTrailingStopAction : nextDCALevel?.BuyTrailingStopAction ?? modifiedTradingConfig.BuyDCATrailingStopAction,\n\n                SellEnabled = modifiedTradingConfig.SellEnabled,\n                SellType = modifiedTradingConfig.SellType,\n                SellMargin = currentDCALevel == null ? modifiedTradingConfig.SellMargin : currentDCALevel?.SellMargin ?? modifiedTradingConfig.SellDCAMargin,\n                SellTrailing = currentDCALevel == null ? modifiedTradingConfig.SellTrailing : currentDCALevel?.SellTrailing ?? modifiedTradingConfig.SellDCATrailing,\n                SellTrailingStopMargin = currentDCALevel == null ? modifiedTradingConfig.SellTrailingStopMargin : currentDCALevel?.SellTrailingStopMargin ?? modifiedTradingConfig.SellDCATrailingStopMargin,\n                SellTrailingStopAction = currentDCALevel == null ? modifiedTradingConfig.SellTrailingStopAction : currentDCALevel?.SellTrailingStopAction ?? modifiedTradingConfig.SellDCATrailingStopAction,\n                SellStopLossEnabled = modifiedTradingConfig.SellStopLossEnabled,\n                SellStopLossAfterDCA = modifiedTradingConfig.SellStopLossAfterDCA,\n                SellStopLossMinAge = modifiedTradingConfig.SellStopLossMinAge / Application.Speed,\n                SellStopLossMargin = modifiedTradingConfig.SellStopLossMargin,\n\n                SwapEnabled = modifiedPairConfig.SwapEnabled,\n                SwapSignalRules = modifiedPairConfig.SwapSignalRules,\n                SwapTimeout = (int)Math.Round(modifiedPairConfig.SwapTimeout / Application.Speed),\n\n                ArbitrageEnabled = modifiedPairConfig.ArbitrageEnabled,\n                ArbitrageMarkets = modifiedPairConfig.ArbitrageMarkets,\n                ArbitrageType = modifiedPairConfig.ArbitrageType,\n                ArbitrageBuyMultiplier = modifiedPairConfig.ArbitrageBuyMultiplier,\n                ArbitrageSellMultiplier = modifiedPairConfig.ArbitrageSellMultiplier,\n                ArbitrageSignalRules = modifiedPairConfig.ArbitrageSignalRules,\n\n                CurrentDCAMargin = currentDCALevel?.Margin,\n                NextDCAMargin = nextDCALevel?.Margin\n            };\n        }\n\n        private DCALevel GetCurrentDCALevel(ITradingPair tradingPair, List<DCALevel> dcaLevels)\n        {\n            if (tradingPair != null && tradingPair.DCALevel > 0 && dcaLevels.Count >= tradingPair.DCALevel)\n            {\n                return dcaLevels[tradingPair.DCALevel - 1];\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n        private DCALevel GetNextDCALevel(ITradingPair tradingPair, List<DCALevel> dcaLevels, bool repeatLastDCALevel)\n        {\n            if (tradingPair != null && dcaLevels.Count > 0)\n            {\n                if (dcaLevels.Count >= tradingPair.DCALevel + 1)\n                {\n                    return dcaLevels[tradingPair.DCALevel];\n                }\n                else if (repeatLastDCALevel)\n                {\n                    return dcaLevels[dcaLevels.Count - 1];\n                }\n                else\n                {\n                    return null;\n                }\n            }\n            else\n            {\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Trading/TimedTasks/TradingTimedTask.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace IntelliTrader.Trading\n{\n    public class TradingTimedTask : HighResolutionTimedTask\n    {\n        public bool LoggingEnabled { get; set; } = true;\n\n        private readonly ILoggingService loggingService;\n        private readonly INotificationService notificationService;\n        private readonly IHealthCheckService healthCheckService;\n        private readonly ISignalsService signalsService;\n        private readonly ITradingService tradingService;\n        private readonly IOrderingService orderingService;\n\n        private readonly ConcurrentDictionary<string, BuyTrailingInfo> trailingBuys = new ConcurrentDictionary<string, BuyTrailingInfo>();\n        private readonly ConcurrentDictionary<string, SellTrailingInfo> trailingSells = new ConcurrentDictionary<string, SellTrailingInfo>();\n\n        public TradingTimedTask(ILoggingService loggingService, INotificationService notificationService,\n            IHealthCheckService healthCheckService, ISignalsService signalsService, IOrderingService orderingService, ITradingService tradingService)\n        {\n            this.loggingService = loggingService;\n            this.notificationService = notificationService;\n            this.healthCheckService = healthCheckService;\n            this.signalsService = signalsService;\n            this.orderingService = orderingService;\n            this.tradingService = tradingService;\n        }\n\n        protected override void Run()\n        {\n            ProcessTradingPairs();\n        }\n\n        public void InitiateBuy(BuyOptions options)\n        {\n            IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);\n            if (!options.ManualOrder && pairConfig.BuyTrailing != 0)\n            {\n                if (!trailingBuys.ContainsKey(options.Pair))\n                {\n                    StopTrailingSell(options.Pair);\n                    decimal currentPrice = tradingService.GetPrice(options.Pair);\n                    decimal currentMargin = 0;\n\n                    var trailingInfo = new BuyTrailingInfo\n                    {\n                        BuyOptions = options,\n                        Trailing = pairConfig.BuyTrailing,\n                        TrailingStopMargin = pairConfig.BuyTrailingStopMargin,\n                        TrailingStopAction = pairConfig.BuyTrailingStopAction,\n                        InitialPrice = currentPrice,\n                        LastTrailingMargin = currentMargin,\n                        BestTrailingMargin = currentMargin\n                    };\n\n                    if (trailingBuys.TryAdd(options.Pair, trailingInfo))\n                    {\n                        if (LoggingEnabled)\n                        {\n                            ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair);\n                            loggingService.Info($\"Start trailing buy {tradingPair?.FormattedName ?? options.Pair}. \" +\n                                $\"Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}\");\n                        }\n                    }\n                }\n            }\n            else\n            {\n                orderingService.PlaceBuyOrder(options);\n            }\n        }\n\n        public void InitiateSell(SellOptions options)\n        {\n            if (tradingService.Account.HasTradingPair(options.Pair))\n            {\n                IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);\n                if (!options.ManualOrder && pairConfig.SellTrailing != 0)\n                {\n                    if (!trailingSells.ContainsKey(options.Pair))\n                    {\n                        StopTrailingBuy(options.Pair);\n                        ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair);\n                        tradingPair.SetCurrentValues(tradingService.GetPrice(options.Pair), tradingService.Exchange.GetPriceSpread(options.Pair));\n\n                        var trailingInfo = new SellTrailingInfo\n                        {\n                            SellOptions = options,\n                            SellMargin = pairConfig.SellMargin,\n                            Trailing = pairConfig.SellTrailing,\n                            TrailingStopMargin = pairConfig.SellTrailingStopMargin,\n                            TrailingStopAction = pairConfig.SellTrailingStopAction,\n                            InitialPrice = tradingPair.CurrentPrice,\n                            LastTrailingMargin = tradingPair.CurrentMargin,\n                            BestTrailingMargin = tradingPair.CurrentMargin\n                        };\n\n                        if (trailingSells.TryAdd(options.Pair, trailingInfo))\n                        {\n                            if (LoggingEnabled)\n                            {\n                                loggingService.Info($\"Start trailing sell {tradingPair.FormattedName}. \" +\n                                    $\"Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}\");\n                            }\n                        }\n                    }\n                }\n                else\n                {\n                    orderingService.PlaceSellOrder(options);\n                }\n            }\n            else\n            {\n                loggingService.Info($\"Cancel initiate sell for {options.Pair}. Reason: pair does not exist\");\n            }\n        }\n\n        public void ProcessTradingPairs()\n        {\n            int traidingPairsCount = 0;\n\n            foreach (var tradingPair in tradingService.Account.GetTradingPairs())\n            {\n                IPairConfig pairConfig = tradingService.GetPairConfig(tradingPair.Pair);\n                tradingPair.SetCurrentValues(tradingService.GetPrice(tradingPair.Pair), tradingService.Exchange.GetPriceSpread(tradingPair.Pair));\n                tradingPair.Metadata.TradingRules = pairConfig.Rules.ToList();\n                tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null ? signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null;\n                tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating();\n\n                if (trailingSells.TryGetValue(tradingPair.Pair, out SellTrailingInfo sellTrailingInfo))\n                {\n                    if (pairConfig.SellEnabled)\n                    {\n                        if (Math.Round(tradingPair.CurrentMargin, 1) != Math.Round(sellTrailingInfo.LastTrailingMargin, 1))\n                        {\n                            if (LoggingEnabled)\n                            {\n                                loggingService.Info($\"Continue trailing sell {tradingPair.FormattedName}. \" +\n                                    $\"Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}\");\n                            }\n                        }\n\n                        if (tradingPair.CurrentMargin <= sellTrailingInfo.TrailingStopMargin || tradingPair.CurrentMargin <\n                            (sellTrailingInfo.BestTrailingMargin - sellTrailingInfo.Trailing))\n                        {\n                            StopTrailingSell(tradingPair.Pair);\n\n                            if (tradingPair.CurrentMargin > 0 || sellTrailingInfo.SellMargin < 0)\n                            {\n                                if (sellTrailingInfo.TrailingStopAction == SellTrailingStopAction.Sell || tradingPair.CurrentMargin > sellTrailingInfo.TrailingStopMargin)\n                                {\n                                    orderingService.PlaceSellOrder(sellTrailingInfo.SellOptions);\n                                }\n                                else\n                                {\n                                    if (LoggingEnabled)\n                                    {\n                                        loggingService.Info($\"Stop trailing sell {tradingPair.FormattedName}. Reason: stop margin reached\");\n                                    }\n                                }\n                            }\n                            else\n                            {\n                                if (LoggingEnabled)\n                                {\n                                    loggingService.Info($\"Stop trailing sell {tradingPair.FormattedName}. Reason: negative margin\");\n                                }\n                            }\n                        }\n                        else\n                        {\n                            sellTrailingInfo.LastTrailingMargin = tradingPair.CurrentMargin;\n                            if (tradingPair.CurrentMargin > sellTrailingInfo.BestTrailingMargin)\n                            {\n                                sellTrailingInfo.BestTrailingMargin = tradingPair.CurrentMargin;\n                            }\n                        }\n                    }\n                    else\n                    {\n                        StopTrailingSell(tradingPair.Pair);\n                    }\n                }\n                else\n                {\n                    if (pairConfig.SellEnabled && tradingPair.CurrentMargin >= pairConfig.SellMargin)\n                    {\n                        InitiateSell(new SellOptions(tradingPair.Pair));\n                    }\n                    else if (pairConfig.SellEnabled && pairConfig.SellStopLossEnabled &&\n                        tradingPair.CurrentMargin <= pairConfig.SellStopLossMargin &&\n                        tradingPair.CurrentAge >= pairConfig.SellStopLossMinAge &&\n                        (pairConfig.NextDCAMargin == null || !pairConfig.SellStopLossAfterDCA))\n                    {\n                        if (LoggingEnabled)\n                        {\n                            loggingService.Info($\"Stop loss triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}\");\n                        }\n                        orderingService.PlaceSellOrder(new SellOptions(tradingPair.Pair));\n                    }\n                    else if (pairConfig.NextDCAMargin != null && pairConfig.BuyEnabled && pairConfig.NextDCAMargin != null &&\n                        !trailingBuys.ContainsKey(tradingPair.Pair) && !trailingSells.ContainsKey(tradingPair.Pair))\n                    {\n                        if (tradingPair.CurrentMargin <= pairConfig.NextDCAMargin)\n                        {\n                            var buyOptions = new BuyOptions(tradingPair.Pair)\n                            {\n                                MaxCost = tradingPair.Cost * pairConfig.BuyMultiplier,\n                                IgnoreExisting = true\n                            };\n\n                            if (tradingService.CanBuy(buyOptions, message: out string message))\n                            {\n                                if (LoggingEnabled)\n                                {\n                                    loggingService.Info($\"DCA triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}, \" +\n                                        $\"Level: {pairConfig.NextDCAMargin:0.00}, Multiplier: {pairConfig.BuyMultiplier}\");\n                                }\n                                InitiateBuy(buyOptions);\n                            }\n                        }\n                    }\n                }\n\n                traidingPairsCount++;\n            }\n\n            foreach (var kvp in trailingBuys)\n            {\n                string pair = kvp.Key;\n                BuyTrailingInfo buyTrailingInfo = kvp.Value;\n                ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair);\n                IPairConfig pairConfig = tradingService.GetPairConfig(pair);\n                decimal currentPrice = tradingService.GetPrice(pair);\n                decimal currentMargin = Utils.CalculatePercentage(buyTrailingInfo.InitialPrice, currentPrice);\n\n                if (pairConfig.BuyEnabled)\n                {\n                    if (Math.Round(currentMargin, 1) != Math.Round(buyTrailingInfo.LastTrailingMargin, 1))\n                    {\n                        if (LoggingEnabled)\n                        {\n                            loggingService.Info($\"Continue trailing buy {tradingPair?.FormattedName ?? pair}. Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}\");\n                        }\n                    }\n\n                    if (currentMargin >= buyTrailingInfo.TrailingStopMargin || currentMargin > (buyTrailingInfo.BestTrailingMargin - buyTrailingInfo.Trailing))\n                    {\n                        StopTrailingBuy(pair);\n\n                        if (buyTrailingInfo.TrailingStopAction == BuyTrailingStopAction.Buy || currentMargin < buyTrailingInfo.TrailingStopMargin)\n                        {\n                            orderingService.PlaceBuyOrder(buyTrailingInfo.BuyOptions);\n                        }\n                        else\n                        {\n                            if (LoggingEnabled)\n                            {\n                                loggingService.Info($\"Stop trailing buy {tradingPair?.FormattedName ?? pair}. Reason: stop margin reached\");\n                            }\n                        }\n                    }\n                    else\n                    {\n                        buyTrailingInfo.LastTrailingMargin = currentMargin;\n                        if (currentMargin < buyTrailingInfo.BestTrailingMargin)\n                        {\n                            buyTrailingInfo.BestTrailingMargin = currentMargin;\n                        }\n                    }\n                }\n                else\n                {\n                    StopTrailingBuy(pair);\n                }\n            }\n\n            healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TradingPairsProcessed,\n                $\"Pairs: {traidingPairsCount}, Trailing buys: {trailingBuys.Count}, Trailing sells: {trailingSells.Count}\");\n        }\n\n        public List<string> GetTrailingBuys()\n        {\n            return trailingBuys.Keys.ToList();\n        }\n\n        public List<string> GetTrailingSells()\n        {\n            return trailingSells.Keys.ToList();\n        }\n\n        public void StopTrailing()\n        {\n            trailingBuys.Clear();\n            trailingSells.Clear();\n        }\n\n        public void StopTrailingBuy(string pair)\n        {\n            trailingBuys.TryRemove(pair, out BuyTrailingInfo buyTrailingInfo);\n        }\n\n        public void StopTrailingSell(string pair)\n        {\n            trailingSells.TryRemove(pair, out SellTrailingInfo sellTrailingInfo);\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/AppModule.cs",
    "content": "﻿using Autofac;\nusing IntelliTrader.Core;\n\nnamespace IntelliTrader.Web\n{\n    public class AppModule : Module\n    {\n        protected override void Load(ContainerBuilder builder)\n        {\n            builder.RegisterType<WebService>().As<IWebService>().As<IConfigurableService>().Named<IConfigurableService>(Constants.ServiceNames.WebService).SingleInstance();\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Controllers/HomeController.cs",
    "content": "﻿using IntelliTrader.Core;\nusing IntelliTrader.Web.Models;\nusing Microsoft.AspNetCore.Mvc;\nusing Newtonsoft.Json;\nusing System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Text.RegularExpressions;\nusing Microsoft.AspNetCore.Authorization;\nusing System.Security.Claims;\nusing Microsoft.AspNetCore.Authentication;\nusing Microsoft.AspNetCore.Authentication.Cookies;\nusing Microsoft.Extensions.Primitives;\nusing System.Threading.Tasks;\nusing System.Security.Cryptography;\nusing System.Text;\n\nnamespace IntelliTrader.Web.Controllers\n{\n    [Authorize]\n    public class HomeController : Controller\n    {\n        #region Authentication\n\n        [AllowAnonymous]\n        public async Task<IActionResult> Login()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            if (coreService.Config.PasswordProtected)\n            {\n                var model = new LoginViewModel\n                {\n                    RememberMe = true\n                };\n                return View(model);\n            }\n            else\n            {\n                return await PerformLogin(true);\n            }\n        }\n\n        [HttpPost]\n        [AllowAnonymous]\n        public async Task<IActionResult> Login(LoginViewModel model)\n        {\n            if (ModelState.IsValid)\n            {\n                var coreService = Application.Resolve<ICoreService>();\n                var isValid = !coreService.Config.PasswordProtected || ComputeMD5Hash(model.Password).Equals(coreService.Config.Password, StringComparison.InvariantCultureIgnoreCase);\n                if (!isValid)\n                {\n                    ModelState.AddModelError(\"Password\", \"Invalid Password\");\n                    return View(model);\n                }\n                else\n                {\n                    return await PerformLogin(model.RememberMe);\n                }\n            }\n            else\n            {\n                return View(model);\n            }\n        }\n\n        [AllowAnonymous]\n        public async Task<IActionResult> Logout()\n        {\n            await HttpContext.SignOutAsync();\n            return RedirectToAction(nameof(Login));\n        }\n\n        private async Task<IActionResult> PerformLogin(bool persistent)\n        {\n            var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);\n            var name = \"user\";\n            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, name));\n            identity.AddClaim(new Claim(ClaimTypes.Name, name));\n            var principal = new ClaimsPrincipal(identity);\n            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = persistent });\n\n            if (Request.Query.TryGetValue(\"ReturnUrl\", out StringValues url))\n            {\n                return RedirectToAction(url);\n            }\n            else\n            {\n                return RedirectToAction(nameof(Index));\n            }\n        }\n\n        private string ComputeMD5Hash(string input)\n        {\n            if (string.IsNullOrEmpty(input))\n            {\n                throw new ArgumentNullException(nameof(input));\n            }\n\n            using (var md5 = MD5.Create())\n            {\n                byte[] inputBytes = Encoding.ASCII.GetBytes(input);\n                byte[] hash = md5.ComputeHash(inputBytes);\n\n                var sb = new StringBuilder();\n                for (int i = 0; i < hash.Length; i++)\n                {\n                    sb.Append(hash[i].ToString(\"X2\"));\n                }\n                return sb.ToString();\n            }\n        }\n\n        #endregion Authentication\n\n        public IActionResult Index()\n        {\n            return Dashboard();\n        }\n\n        public IActionResult Dashboard()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n            var model = new DashboardViewModel\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode\n            };\n            return View(nameof(Dashboard), model);\n        }\n\n        public IActionResult Market()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n            var model = new MarketViewModel\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode\n            };\n            return View(model);\n        }\n\n        public IActionResult Stats()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n            var tradingService = Application.Resolve<ITradingService>();\n            var accountInitialBalance = tradingService.Config.VirtualTrading ? tradingService.Config.VirtualAccountInitialBalance : tradingService.Config.AccountInitialBalance;\n            var accountInitialBalanceDate = tradingService.Config.VirtualTrading ? DateTimeOffset.Now.AddDays(-30) : tradingService.Config.AccountInitialBalanceDate;\n\n            var model = new StatsViewModel\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode,\n                TimezoneOffset = coreService.Config.TimezoneOffset,\n                AccountInitialBalance = accountInitialBalance,\n                AccountBalance = tradingService.Account.GetTotalBalance(),\n                Market = tradingService.Config.Market,\n                Balances = new Dictionary<DateTimeOffset, decimal>(),\n                Trades = GetTrades()\n            };\n\n            foreach (var kvp in model.Trades.OrderBy(k => k.Key))\n            {\n                var date = kvp.Key;\n                var trades = kvp.Value;\n\n                model.Balances[date] = accountInitialBalance;\n\n                if (date > accountInitialBalanceDate.Date)\n                {\n                    for (int d = 1; d < (int)(date - accountInitialBalanceDate.Date).TotalDays; d++)\n                    {\n                        var prevDate = date.AddDays(-d);\n                        if (model.Trades.ContainsKey(prevDate))\n                        {\n                            model.Balances[date] += model.Trades[prevDate].Where(t => !t.IsSwap).Sum(t => t.Profit);\n                        }\n                    }\n                }\n            }\n\n            return View(model);\n        }\n\n        public IActionResult Rules()\n        {\n            var allTades = GetTrades();\n            var signalRuleStats = new Dictionary<string, SignalRuleStats>();\n            foreach (var trade in allTades.Values.SelectMany(t => t))\n            {\n                if (trade.IsSuccessful)\n                {\n                    var signalRule = trade?.Metadata?.SignalRule;\n                    if (!String.IsNullOrWhiteSpace(signalRule))\n                    {\n                        if (!signalRuleStats.TryGetValue(signalRule, out SignalRuleStats ruleStats))\n                        {\n                            ruleStats = new SignalRuleStats();\n                            signalRuleStats.Add(signalRule, ruleStats);\n                        }\n\n                        if (!trade.IsSwap)\n                        {\n                            ruleStats.TotalCost += trade.Cost;\n                            ruleStats.TotalProfit += trade.Profit;\n                            decimal margin = trade.Profit / (trade.Cost + (trade.Metadata?.AdditionalCosts ?? 0)) * 100;\n                            if (trade.OrderDates.Count == 1)\n                            {\n                                ruleStats.Margin.Add(margin);\n                            }\n                            else\n                            {\n                                ruleStats.MarginDCA.Add(margin);\n                            }\n                        }\n                        else\n                        {\n                            ruleStats.TotalSwaps++;\n                        }\n\n                        ruleStats.TotalTrades++;\n                        ruleStats.TotalOrders += trade.OrderDates.Count;\n                        ruleStats.TotalFees += trade.FeesTotal;\n                        ruleStats.Age.Add((trade.SellDate - trade.OrderDates.Min()).TotalDays);\n                        ruleStats.DCA.Add((trade.OrderDates.Count - 1) + (trade.Metadata?.AdditionalDCALevels ?? 0));\n                    }\n                }\n            }\n\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n            var model = new RulesViewModel\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode,\n                SignalRuleStats = signalRuleStats\n            };\n\n            return View(model);\n        }\n\n        public IActionResult Trades(DateTimeOffset id)\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n            var model = new TradesViewModel()\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode,\n                TimezoneOffset = coreService.Config.TimezoneOffset,\n                Date = id,\n                Trades = GetTrades(id).Values.FirstOrDefault() ?? new List<TradeResult>()\n            };\n\n            return View(model);\n        }\n\n        public IActionResult Settings()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n            var tradingService = Application.Resolve<ITradingService>();\n            var allConfigurableServices = Application.Resolve<IEnumerable<IConfigurableService>>();\n\n            var model = new SettingsViewModel()\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode,\n                BuyEnabled = tradingService.Config.BuyEnabled,\n                BuyDCAEnabled = tradingService.Config.BuyDCAEnabled,\n                SellEnabled = tradingService.Config.SellEnabled,\n                TradingSuspended = tradingService.IsTradingSuspended,\n                HealthCheckEnabled = coreService.Config.HealthCheckEnabled,\n                Configs = allConfigurableServices.Where(s => !s.GetType().Name.Contains(Constants.ServiceNames.BacktestingService)).OrderBy(s => s.ServiceName).ToDictionary(s => s.ServiceName, s => Application.ConfigProvider.GetSectionJson(s.ServiceName))\n            };\n\n            return View(model);\n        }\n\n        public IActionResult Log()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n            var loggingService = Application.Resolve<ILoggingService>();\n\n            var model = new LogViewModel()\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode,\n                LogEntries = loggingService.GetLogEntries().Reverse().Take(500)\n            };\n\n            return View(model);\n        }\n\n        public IActionResult Help()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var webService = Application.Resolve<IWebService>();\n\n            var model = new HelpViewModel()\n            {\n                InstanceName = coreService.Config.InstanceName,\n                Version = coreService.Version,\n                ReadOnlyMode = webService.Config.ReadOnlyMode\n            };\n\n            return View(model);\n        }\n\n\n\n        public IActionResult Status()\n        {\n            var loggingService = Application.Resolve<ILoggingService>();\n            var tradingService = Application.Resolve<ITradingService>();\n            var signalsService = Application.Resolve<ISignalsService>();\n            var healthCheckService = Application.Resolve<IHealthCheckService>();\n\n            var status = new\n            {\n                Balance = tradingService.Account.GetBalance(),\n                GlobalRating = signalsService.GetGlobalRating()?.ToString(\"0.000\") ?? \"N/A\",\n                TrailingBuys = tradingService.GetTrailingBuys(),\n                TrailingSells = tradingService.GetTrailingSells(),\n                TrailingSignals = signalsService.GetTrailingSignals(),\n                TradingSuspended = tradingService.IsTradingSuspended,\n                HealthChecks = healthCheckService.GetHealthChecks().OrderBy(c => c.Name),\n                LogEntries = loggingService.GetLogEntries().Reverse().Take(5)\n            };\n            return Json(status);\n        }\n\n        public IActionResult SignalNames()\n        {\n            var signalsService = Application.Resolve<ISignalsService>();\n            return Json(signalsService.GetSignalNames());\n        }\n\n        [HttpPost]\n        public IActionResult TradingPairs()\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var tradingService = Application.Resolve<ITradingService>();\n\n            var tradingPairs = from tradingPair in tradingService.Account.GetTradingPairs()\n                               let pairConfig = tradingService.GetPairConfig(tradingPair.Pair)\n                               select new\n                               {\n                                   Name = tradingPair.Pair,\n                                   DCA = tradingPair.DCALevel,\n                                   TradingViewName = $\"{tradingService.Config.Exchange.ToUpperInvariant()}:{tradingPair.Pair}\",\n                                   Margin = tradingPair.CurrentMargin.ToString(\"0.00\"),\n                                   Target = pairConfig.SellMargin.ToString(\"0.00\"),\n                                   CurrentPrice = tradingPair.CurrentPrice.ToString(\"0.00000000\"),\n                                   CurrentSpread = tradingPair.CurrentSpread.ToString(\"0.00\"),\n                                   BoughtPrice = tradingPair.AveragePrice.ToString(\"0.00000000\"),\n                                   Cost = tradingPair.Cost.ToString(\"0.00000000\"),\n                                   CurrentCost = tradingPair.CurrentCost.ToString(\"0.00000000\"),\n                                   Amount = tradingPair.Amount.ToString(\"0.########\"),\n                                   OrderDates = tradingPair.OrderDates.Select(d => d.ToOffset(TimeSpan.FromHours(coreService.Config.TimezoneOffset)).ToString(\"yyyy-MM-dd HH:mm:ss\")),\n                                   OrderIds = tradingPair.OrderIds,\n                                   Age = tradingPair.CurrentAge.ToString(\"0.00\"),\n                                   CurrentRating = tradingPair.Metadata.CurrentRating?.ToString(\"0.000\") ?? \"N/A\",\n                                   BoughtRating = tradingPair.Metadata.BoughtRating?.ToString(\"0.000\") ?? \"N/A\",\n                                   SignalRule = tradingPair.Metadata.SignalRule ?? \"N/A\",\n                                   SwapPair = tradingPair.Metadata.SwapPair,\n                                   TradingRules = pairConfig.Rules,\n                                   IsTrailingSell = tradingService.GetTrailingSells().Contains(tradingPair.Pair),\n                                   IsTrailingBuy = tradingService.GetTrailingBuys().Contains(tradingPair.Pair),\n                                   LastBuyMargin = tradingPair.Metadata.LastBuyMargin?.ToString(\"0.00\") ?? \"N/A\",\n                                   Config = pairConfig\n                               };\n\n            return Json(tradingPairs);\n        }\n\n        [HttpPost]\n        public IActionResult MarketPairs(List<string> signalsFilter)\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var tradingService = Application.Resolve<ITradingService>();\n            var signalsService = Application.Resolve<ISignalsService>();\n\n            var allSignals = signalsService.GetAllSignals();\n            if (allSignals != null)\n            {\n                if (signalsFilter.Count > 0)\n                {\n                    allSignals = allSignals.Where(s => signalsFilter.Contains(s.Name));\n                }\n\n                var groupedSignals = allSignals.GroupBy(s => s.Pair).ToDictionary(g => g.Key, g => g.AsEnumerable());\n\n                var marketPairs = from signalGroup in groupedSignals\n                                  let pair = signalGroup.Key\n                                  let pairConfig = tradingService.GetPairConfig(pair)\n                                  select new\n                                  {\n                                      Name = pair,\n                                      TradingViewName = $\"{tradingService.Config.Exchange.ToUpperInvariant()}:{pair}\",\n                                      VolumeList = signalGroup.Value.Select(s => new { s.Name, s.Volume }),\n                                      VolumeChangeList = signalGroup.Value.Select(s => new { s.Name, s.VolumeChange }),\n                                      Price = tradingService.GetPrice(pair).ToString(\"0.00000000\"),\n                                      PriceChangeList = signalGroup.Value.Select(s => new { s.Name, s.PriceChange }),\n                                      RatingList = signalGroup.Value.Select(s => new { s.Name, s.Rating }),\n                                      RatingChangeList = signalGroup.Value.Select(s => new { s.Name, s.RatingChange }),\n                                      VolatilityList = signalGroup.Value.Select(s => new { s.Name, s.Volatility }),\n                                      Spread = tradingService.Exchange.GetPriceSpread(pair).ToString(\"0.00\"),\n                                      ArbitrageList = from market in Enum.GetNames(typeof(ArbitrageMarket)).Where(m => m != tradingService.Config.Market)\n                                                      let arbitrage = tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, new List<ArbitrageMarket> { Enum.Parse<ArbitrageMarket>(market) })\n                                                      select new\n                                                      {\n                                                          Name = $\"{arbitrage.Market}-{arbitrage.Type.ToString()[0]}\",\n                                                          Arbitrage = arbitrage.IsAssigned ? arbitrage.Percentage.ToString(\"0.00\") : \"N/A\"\n                                                      },\n                                      SignalRules = signalsService.GetTrailingInfo(pair)?.Select(ti => ti.Rule.Name) ?? new string[0],\n                                      HasTradingPair = tradingService.Account.HasTradingPair(pair),\n                                      Config = pairConfig\n                                  };\n\n                return Json(marketPairs);\n            }\n            else\n            {\n                return Json(null);\n            }\n        }\n\n        [HttpPost]\n        public IActionResult Settings(SettingsViewModel model)\n        {\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode)\n            {\n                var coreService = Application.Resolve<ICoreService>();\n                var tradingService = Application.Resolve<ITradingService>();\n\n                coreService.Config.HealthCheckEnabled = model.HealthCheckEnabled;\n                tradingService.Config.BuyEnabled = model.BuyEnabled;\n                tradingService.Config.BuyDCAEnabled = model.BuyDCAEnabled;\n                tradingService.Config.SellEnabled = model.SellEnabled;\n\n                if (model.TradingSuspended)\n                {\n                    tradingService.SuspendTrading();\n                }\n                else\n                {\n                    tradingService.ResumeTrading();\n                }\n                return Settings();\n            }\n            else\n            {\n                return Settings();\n            }\n        }\n\n        [HttpPost]\n        public IActionResult SaveConfig()\n        {\n            string configName = Request.Form[\"name\"].ToString();\n            string configDefinition = Request.Form[\"definition\"].ToString();\n\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(configName) && !String.IsNullOrWhiteSpace(configDefinition))\n            {\n                Application.ConfigProvider.SetSectionJson(configName, configDefinition);\n                return new OkResult();\n            }\n            else\n            {\n                return new BadRequestResult();\n            }\n        }\n\n        [HttpPost]\n        public IActionResult Sell()\n        {\n            string pair = Request.Form[\"pair\"].ToString();\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode && pair != null && decimal.TryParse(Request.Form[\"amount\"], out decimal amount) && amount > 0)\n            {\n                var tradingService = Application.Resolve<ITradingService>();\n                tradingService.Sell(new SellOptions(pair)\n                {\n                    Amount = amount,\n                    ManualOrder = true\n                });\n                return new OkResult();\n            }\n            else\n            {\n                return new BadRequestResult();\n            }\n        }\n\n        [HttpPost]\n        public IActionResult Buy()\n        {\n            string pair = Request.Form[\"pair\"].ToString();\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(pair) && decimal.TryParse(Request.Form[\"amount\"], out decimal amount) && amount > 0)\n            {\n                var tradingService = Application.Resolve<ITradingService>();\n                tradingService.Buy(new BuyOptions(pair)\n                {\n                    Amount = amount,\n                    IgnoreExisting = true,\n                    ManualOrder = true\n                });\n                return new OkResult();\n            }\n            else\n            {\n                return new BadRequestResult();\n            }\n        }\n\n        [HttpPost]\n        public IActionResult BuyDefault()\n        {\n            string pair = Request.Form[\"pair\"].ToString();\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(pair))\n            {\n                var signalsService = Application.Resolve<ISignalsService>();\n                var tradingService = Application.Resolve<ITradingService>();\n                tradingService.Buy(new BuyOptions(pair)\n                {\n                    MaxCost = tradingService.GetPairConfig(pair).BuyMaxCost,\n                    IgnoreExisting = true,\n                    ManualOrder = true,\n                    Metadata = new OrderMetadata\n                    {\n                        BoughtGlobalRating = signalsService.GetGlobalRating()\n                    }\n                });\n                return new OkResult();\n            }\n            else\n            {\n                return new BadRequestResult();\n            }\n        }\n\n        [HttpPost]\n        public IActionResult Swap()\n        {\n            string pair = Request.Form[\"pair\"].ToString();\n            string swap = Request.Form[\"swap\"].ToString();\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(pair) && !String.IsNullOrWhiteSpace(swap))\n            {\n                var tradingService = Application.Resolve<ITradingService>();\n                tradingService.Swap(new SwapOptions(pair, swap, new OrderMetadata())\n                {\n                    ManualOrder = true\n                });\n                return new OkResult();\n            }\n            else\n            {\n                return new BadRequestResult();\n            }\n        }\n\n        public IActionResult RefreshAccount()\n        {\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode)\n            {\n                var tradingService = Application.Resolve<ITradingService>();\n                tradingService.Account.Refresh();\n                return new OkResult();\n            }\n            else\n            {\n                return new BadRequestResult();\n            }\n        }\n\n        public IActionResult RestartServices()\n        {\n            if (!Application.Resolve<IWebService>().Config.ReadOnlyMode)\n            {\n                var coreService = Application.Resolve<ICoreService>();\n                coreService.Restart();\n                return new OkResult();\n            }\n            else\n            {\n                return new BadRequestResult();\n            }\n        }\n\n        private Dictionary<DateTimeOffset, List<TradeResult>> GetTrades(DateTimeOffset? date = null)\n        {\n            var coreService = Application.Resolve<ICoreService>();\n            var logsPath = Path.Combine(Directory.GetCurrentDirectory(), \"log\");\n            var tradeResultPattern = new Regex($\"{nameof(TradeResult)} (?<data>\\\\{{.*\\\\}})\", RegexOptions.Compiled);\n            var trades = new Dictionary<DateTimeOffset, List<TradeResult>>();\n\n            if (Directory.Exists(logsPath))\n            {\n                foreach (var tradesLogFilePath in Directory.EnumerateFiles(logsPath, \"*-trades.txt\", SearchOption.TopDirectoryOnly))\n                {\n                    IEnumerable<string> logLines = Utils.ReadAllLinesWriteSafe(tradesLogFilePath);\n                    foreach (var logLine in logLines)\n                    {\n                        var match = tradeResultPattern.Match(logLine);\n                        if (match.Success)\n                        {\n                            var data = match.Groups[\"data\"].ToString();\n                            var json = Utils.FixInvalidJson(data.Replace(nameof(OrderMetadata), \"\"))\n                                .Replace(\"AveragePricePaid\", nameof(ITradeResult.AveragePrice)); // Old property migration\n\n                            TradeResult tradeResult = JsonConvert.DeserializeObject<TradeResult>(json);\n                            if (tradeResult.IsSuccessful && tradeResult.Metadata?.IsTransitional != true)\n                            {\n                                DateTimeOffset tradeDate = tradeResult.SellDate.ToOffset(TimeSpan.FromHours(coreService.Config.TimezoneOffset)).Date;\n                                if (date == null || date == tradeDate)\n                                {\n                                    if (!trades.ContainsKey(tradeDate))\n                                    {\n                                        trades.Add(tradeDate, new List<TradeResult>());\n                                    }\n                                    trades[tradeDate].Add(tradeResult);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            return trades;\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/IntelliTrader.Web.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n    <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>\n    <ApplicationIcon />\n    <OutputType>Library</OutputType>\n    <StartupObject />\n    <TypeScriptToolsVersion>2.8</TypeScriptToolsVersion>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Remove=\"Static\\favicon.ico\" />\n    <None Remove=\"Static\\Help\\index.md\" />\n    <None Remove=\"Static\\Help\\navigation.md\" />\n    <None Remove=\"Static\\Images\\favicon.ico\" />\n    <None Remove=\"Static\\Images\\logo.png\" />\n    <None Remove=\"Static\\Scripts\\intellitrader.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\bootstrap-multiselect.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\bootstrap.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\datatables.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\highlight.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\jquery-3.3.1.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\jquery.colorbox.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\jsoneditor.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\mdwiki.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\mdwiki.min.js\" />\n    <None Remove=\"Static\\Scripts\\Vendor\\popper.min.js\" />\n    <None Remove=\"Static\\Scripts\\Views\\dashboard.js\" />\n    <None Remove=\"Static\\Scripts\\Views\\market.js\" />\n    <None Remove=\"Static\\Scripts\\Views\\rules.js\" />\n    <None Remove=\"Static\\Scripts\\Views\\settings.js\" />\n    <None Remove=\"Static\\Scripts\\Views\\stats.js\" />\n    <None Remove=\"Static\\Scripts\\Views\\trades.js\" />\n    <None Remove=\"Static\\Styles\\intellitrader.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\bootstrap-multiselect.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\bootstrap-theme.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\bootstrap.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\colorbox.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\datatables.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\fontawesome-all.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.eot\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.svg\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.ttf\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.woff\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.woff2\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.eot\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.svg\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.ttf\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.woff\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.woff2\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.eot\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.svg\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.ttf\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.woff\" />\n    <None Remove=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.woff2\" />\n    <None Remove=\"Static\\Styles\\Vendor\\highlight.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\img\\jsoneditor-icons.svg\" />\n    <None Remove=\"Static\\Styles\\Vendor\\jsoneditor.min.css\" />\n    <None Remove=\"Static\\Styles\\Vendor\\mdwiki.min.css\" />\n    <None Remove=\"Static\\Styles\\Views\\dashboard.css\" />\n    <None Remove=\"Static\\Styles\\Views\\help.css\" />\n    <None Remove=\"Static\\Styles\\Views\\market.css\" />\n    <None Remove=\"Static\\Styles\\Views\\rules.css\" />\n    <None Remove=\"Static\\Styles\\Views\\settings.css\" />\n    <None Remove=\"Static\\Styles\\Views\\stats.css\" />\n    <None Remove=\"Static\\Styles\\Views\\trades.css\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Content Include=\"Static\\Images\\favicon.ico\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Images\\logo.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\mdwiki.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n\n  <ItemGroup>\n    <Content Include=\"Static\\Help\\index.md\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Help\\navigation.md\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\intellitrader.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\bootstrap-multiselect.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\bootstrap.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\datatables.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\highlight.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\jquery-3.3.1.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\jquery.colorbox.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\jsoneditor.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\mdwiki.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Vendor\\popper.min.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Views\\dashboard.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Views\\market.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Views\\rules.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Views\\settings.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Views\\stats.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Scripts\\Views\\trades.js\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\intellitrader.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\bootstrap-multiselect.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\bootstrap-theme.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\bootstrap.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\colorbox.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\datatables.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\fontawesome-all.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.eot\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.svg\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.ttf\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.woff\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-brands-400.woff2\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.eot\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.svg\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.ttf\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.woff\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-regular-400.woff2\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.eot\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.svg\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.ttf\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.woff\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\Fonts\\fa-solid-900.woff2\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\highlight.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\img\\jsoneditor-icons.svg\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\jsoneditor.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Vendor\\mdwiki.min.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Views\\dashboard.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Views\\help.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Views\\market.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Views\\rules.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Views\\settings.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Views\\stats.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Static\\Styles\\Views\\trades.css\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.AspNetCore.Authentication\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Microsoft.AspNetCore.Authentication.Cookies\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Microsoft.AspNetCore.Diagnostics\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Microsoft.AspNetCore.Mvc\" Version=\"2.1.1\" />\n    <PackageReference Include=\"Microsoft.AspNetCore.Server.Kestrel\" Version=\"2.1.2\" />\n    <PackageReference Include=\"Microsoft.AspNetCore.StaticFiles\" Version=\"2.1.1\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IntelliTrader.Core\\IntelliTrader.Core.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Content Update=\"Static\\Help\\config.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Update=\"Views\\Home\\Rules.cshtml\">\n      <Pack>$(IncludeRazorContentInPack)</Pack>\n    </Content>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "IntelliTrader.Web/Misc/Utils.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.IO;\nusing System.Text.RegularExpressions;\n\nnamespace IntelliTrader.Web\n{\n    public static class Utils\n    {\n        private static Regex fixJsonPattern = new Regex(@\"\\w[^,{]+[\\w\"\"]\", RegexOptions.Compiled);\n\n        public static string FixInvalidJson(string json)\n        {\n            string fixedJson = fixJsonPattern.Replace(json, match =>\n            {\n                string matchString = match.ToString();\n                if (!matchString.EndsWith(\"\\\"\"))\n                {\n                    string[] split = matchString.Split(\": \");\n\n                    if (split.Length == 1)\n                    {\n                        return $\"\\\"{matchString.Trim('\"', ' ')}\\\"\";\n                    }\n                    else\n                    {\n                        string left = split[0].Trim('\"', ' ');\n                        string right = split[1].Trim('\"', ' ');\n\n                        if (right[0] == '[')\n                        {\n                            return $\"\\\"{left}\\\": {right.Replace(\"[\", \"[\\\"\")}\\\"\";\n                        }\n                        else if (right == \"True\" || right == \"False\")\n                        {\n                            return $\"\\\"{left}\\\": \\\"{right.ToLowerInvariant()}\\\"\";\n                        }\n                        else if (right == \"null\")\n                        {\n                            return $\"\\\"{left}\\\": {right}\";\n                        }\n                        else\n                        {\n                            return $\"\\\"{left}\\\": \\\"{right}\\\"\";\n                        }\n                    }\n                }\n                else\n                {\n                    return matchString;\n                }\n            });\n            return fixedJson;\n        }\n\n        public static IEnumerable<string> ReadAllLinesWriteSafe(string path)\n        {\n            using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))\n            using (var sr = new StreamReader(fs))\n            {\n                while (!sr.EndOfStream)\n                {\n                    yield return sr.ReadLine();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/BaseViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class BaseViewModel\n    {\n        public string InstanceName { get; set; }\n        public string Version { get; set; }\n        public bool ReadOnlyMode { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/Config/WebConfig.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace IntelliTrader.Web\n{\n    internal class WebConfig : IWebConfig\n    {\n        public bool Enabled { get; set; }\n        public bool DebugMode { get; set; }\n        public bool ReadOnlyMode { get; set; }\n        public int Port { get; set; }\n        public bool SSLEnabled { get; set; }\n        public string SSLCertPath { get; set; }\n        public string SSLCertPassword { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/DashboardViewModel.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class DashboardViewModel : BaseViewModel\n    {\n        \n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/HelpViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class HelpViewModel : BaseViewModel\n    {\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/LogViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class LogViewModel : BaseViewModel\n    {\n        public IEnumerable<string> LogEntries { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/LoginViewModel.cs",
    "content": "﻿using System.ComponentModel.DataAnnotations;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class LoginViewModel : BaseViewModel\n    {\n        [Required, DataType(DataType.Password)]\n        public string Password { get; set; }\n\n        public bool RememberMe { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/MarketViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class MarketViewModel : BaseViewModel\n    {\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/RulesViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class RulesViewModel : BaseViewModel\n    {\n        public Dictionary<string, SignalRuleStats> SignalRuleStats { get; set; }\n    }\n\n    public class SignalRuleStats\n    {\n        public decimal TotalProfit { get; set; }\n        public decimal TotalFees { get; set; }\n        public decimal TotalCost { get; set; }\n        public int TotalTrades { get; set; }\n        public int TotalOrders { get; set; }\n        public int TotalSwaps { get; set; }\n        public List<double> Age { get; set; } = new List<double>();\n        public List<decimal> Margin { get; set; } = new List<decimal>();\n        public List<decimal> MarginDCA { get; set; } = new List<decimal>();\n        public List<int> DCA { get; set; } = new List<int>();\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/SettingsViewModel.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel.DataAnnotations;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class SettingsViewModel : BaseViewModel\n    {\n        [Display(Name = \"Buy Enabled\")]\n        public bool BuyEnabled { get; set; }\n        [Display(Name = \"Buy DCA Enabled\")]\n        public bool BuyDCAEnabled { get; set; }\n        [Display(Name = \"Sell Enabled\")]\n        public bool SellEnabled { get; set; }\n        [Display(Name = \"Trading Suspended\")]\n        public bool TradingSuspended { get; set; }\n        [Display(Name = \"Health Check Enabled\")]\n        public bool HealthCheckEnabled { get; set; }\n        public Dictionary<string, string> Configs { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/StatsViewModel.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class StatsViewModel : BaseViewModel\n    {\n        public double TimezoneOffset { get; set; }\n        public decimal AccountInitialBalance { get; set; }\n        public decimal AccountBalance { get; set; }\n        public string Market { get; set; }        \n        public Dictionary<DateTimeOffset, List<TradeResult>> Trades { get; set; }\n        public Dictionary<DateTimeOffset, decimal> Balances { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Models/TradesViewModel.cs",
    "content": "﻿using IntelliTrader.Core;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace IntelliTrader.Web.Models\n{\n    public class TradesViewModel : BaseViewModel\n    {\n        public double TimezoneOffset { get; set; }\n        public DateTimeOffset Date { get; set; }\n        public List<TradeResult> Trades { get; set; }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Properties/PublishProfiles/FolderProfile.pubxml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nThis file is used by the publish/package process of your Web project. You can customize the behavior of this process\nby editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121. \n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <WebPublishMethod>FileSystem</WebPublishMethod>\n    <PublishProvider>FileSystem</PublishProvider>\n    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>\n    <LastUsedPlatform>Any CPU</LastUsedPlatform>\n    <SiteUrlToLaunchAfterPublish />\n    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>\n    <ExcludeApp_Data>False</ExcludeApp_Data>\n    <ProjectGuid>802ff51c-e66d-44bc-b26f-c72c68ed9e56</ProjectGuid>\n    <publishUrl>..\\Publish\\bin</publishUrl>\n    <DeleteExistingFiles>False</DeleteExistingFiles>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "IntelliTrader.Web/Services/WebService.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Microsoft.AspNetCore.Hosting;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net;\nusing System.Text;\n\nnamespace IntelliTrader.Web\n{\n    internal class WebService : ConfigrableServiceBase<WebConfig>, IWebService\n    {\n        public override string ServiceName => Constants.ServiceNames.WebService;\n\n        IWebConfig IWebService.Config => Config;\n\n        private readonly ILoggingService loggingService;\n\n        private IWebHost webHost;\n\n        public WebService(ILoggingService loggingService)\n        {\n            this.loggingService = loggingService;\n        }\n\n        public void Start()\n        {\n            loggingService.Info($\"Start Web service (Port: {Config.Port})...\");\n\n            try\n            {\n                var contentRoot = Path.GetFullPath(Directory.GetCurrentDirectory() + @\"/../IntelliTrader.Web\");\n#if RELEASE\n                if (!System.Diagnostics.Debugger.IsAttached)\n                {\n                    contentRoot = Path.Combine(Directory.GetCurrentDirectory(), \"bin\");\n                }\n#endif\n\n                var webHostBuilder = new WebHostBuilder()\n                    .UseContentRoot(contentRoot)\n                    .UseStartup<Startup>()\n                    .UseKestrel(options =>\n                    {\n                        if (Config.SSLEnabled)\n                        {\n                            options.Listen(IPAddress.Any, Config.Port, listenOptions =>\n                            {\n                                listenOptions.UseHttps(Path.Combine(Directory.GetCurrentDirectory(), Config.SSLCertPath), Config.SSLCertPassword);\n                            });\n                        }\n                        else\n                        {\n                            options.Listen(IPAddress.Any, Config.Port);\n                        }\n                    });\n\n                if (Config.DebugMode)\n                {\n                    webHostBuilder.UseEnvironment(\"Development\");\n                }\n                else\n                {\n                    webHostBuilder.UseEnvironment(\"Production\");\n                }\n\n                webHost = webHostBuilder.Build();\n\n                // Suppress WebHost startup messages\n                var consOut = Console.Out;\n                webHost.Start();\n                Console.SetOut(consOut);\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error($\"Unable to start Web service\", ex);\n            }\n\n            loggingService.Info($\"Web service started\");\n        }\n\n        public void Stop()\n        {\n            loggingService.Info($\"Stop Web service...\");\n\n            try\n            {\n                webHost.Dispose();\n                loggingService.Info($\"Web service stopped\");\n            }\n            catch (Exception ex)\n            {\n                loggingService.Error($\"Unable to stop Web service\", ex);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Startup.cs",
    "content": "﻿using IntelliTrader.Core;\nusing Microsoft.AspNetCore.Authentication.Cookies;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.FileProviders;\nusing Newtonsoft.Json.Serialization;\nusing System;\nusing System.IO;\n\nnamespace IntelliTrader.Web\n{\n    public class Startup\n    {\n        public Startup(IConfiguration configuration)\n        {\n            Configuration = configuration;\n        }\n\n        public IConfiguration Configuration { get; }\n\n        public void ConfigureServices(IServiceCollection services)\n        {\n            var coreService = Application.Resolve<ICoreService>();\n\n            services.AddAuthentication(options =>\n            {\n                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;\n                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;\n                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;\n            }).AddCookie(options =>\n            {\n                options.LoginPath = \"/Login\";\n                options.Cookie.Name = $\"{nameof(IntelliTrader)}_{coreService.Config.InstanceName}\";\n            });\n\n            services.AddMvc().AddJsonOptions(opts =>\n            {\n                opts.SerializerSettings.ContractResolver = new DefaultContractResolver();\n            });\n        }\n\n        public void Configure(IApplicationBuilder app, IHostingEnvironment env)\n        {\n            if (env.IsDevelopment())\n            {\n                app.UseDeveloperExceptionPage();\n            }\n            else\n            {\n                app.UseExceptionHandler(\"/Error\");\n            }\n\n            app.UseStaticFiles(new StaticFileOptions\n            {\n                FileProvider = new PhysicalFileProvider(\n                Path.Combine(env.ContentRootPath, \"Static\")),\n                RequestPath = \"/Static\"\n            });\n\n            app.UseAuthentication();\n\n            app.UseMvc(routes =>\n            {\n                routes.MapRoute(\n                    \"Default\",\n                    \"{action}/{id?}\",\n                    new { controller = \"Home\", action = \"Index\" }\n                );\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Static/Help/config.json",
    "content": "{\n  \"title\": \"\",\n  \"additionalFooterText\": \"\",\n  \"useSideMenu\": true,\n  \"lineBreaks\": \"gfm\",\n  \"anchorCharacter\": \"¶\"\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Help/index.md",
    "content": "Help\n===========\n\nConfiguration\n-------------\n\nAll changes to the configuration files will take effect immediately.\n\nConfiguration quick links:\n[Value Types](#Value_Types), [Core](#Core_Configuration), [Web](#Web_Configuration), [Signals](#Signals_Configuration), [Trading](#Trading_Configuration), [Rules](#Rules_Configuration), [Notification](#Notification_Configuration), [Backtesting](#Backtesting_Configuration), [Other](#Other_Configuration)\n\n#### Value Types\n\n|Type|Example|Description|\n|-|:-:|-|\n|Number|0.40|Numeric value|\n|Boolean|true|Boolean value (true/false)|\n|String|Market|String value|\n|Array|[ \"BNBBTC\", \"ETHBTC\" ] |Array of numbers, booleans or strings|\n|Object|{ \"Key\": \"Value\" }|Json object|\n\n#### Core Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|DebugMode|Boolean|true|Enable / disable debug mode|\n|PasswordProtected|Boolean|true|Use password authentication|\n|Password|String|MD5 hash|MD5-encrypted password|\n|InstanceName|String|Main|Must be alphanumeric and should not contain spaces. Used by the web interface & notification service to distinguish between different bot instances (when running multiple)|\n|TimezoneOffset|Number|1|Timezone offset from UTC (in hours), used by stats|\n|HealthCheckEnabled|Boolean|true|Enable / disable health check|\n|HealthCheckInterval|Number|180|Interval to check that all the data is up to date and services are running correctly (in seconds)|\n|HealthCheckSuspendTradingTimeout|Number|900|Suspend trading (disable sells and buys) if any of the health checks did not pass in a specified period of time (in seconds). Set to 0 to continue trading even after health check fails\n|HealthCheckFailuresToRestartServices|Number|3|Restart all services when health check fails for a specified number of times in a row. Set to 0 to disable restart|\n\n#### Web Configuration\n\nRead more about how web interface works in the [web](#Web_Interface) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable web interface|\n|DebugMode|Boolean|true|Enable / disable debug mode|\n|ReadOnlyMode|Boolean|false|Enable / disable read only mode|\n|Port|Number|7000|Port on which to host the web interface|\n|SSLEnabled|Boolean|false|Enable SSL for web interface|\n|SSLCertPath|String|data/cert.pfx|Path to the SSL certificate|\n|SSLCertPassword|String|certpass|Certificate password|\n\n#### Signals Configuration\n\nRead more about how signals work in the [signals](#Signals) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable signals|\n|GlobalRatingSignals|Array|\"TV-5m\",\"TV-15m\",\"TV-1h\"|Signals to calculate the Global Rating from|\n|Definitions|Array|[Signal Definitions](#Signal_Definitions)|Signal source definitions|\n\n###### Signal Definitions\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Name|String|\"TV-15m\"|Signal name|\n|Receiver|String|\"TradingViewCryptoSignalReceiver\"|Signal receiver name|\n|Configuration|Object|[Signal Receiver Configuration](#Signal_Receiver_Configuration)|Signal receiver configuration|\n\n###### Signal Receiver Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|-|-|\n|PollingInterval|Number|7|How often to check for latest signals (in seconds)|\n|SignalPeriod|Number|15|Signal period (in minutes). Possible values: 5, 15, 60, 240, 1440|\n|VolatilityPeriod|String|\"Day\"|Volatility period. Possible values: Day, Week, Month|\n|RequestUrl|String|\"https://scanner.tradingview.com/crypto/scan\"|Request url|\n|RequestData|String|\"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"|Request data|\n\n#### Trading Configuration\n\nRead more about how trading works in the [trading](#Trading) section.\n\n###### General\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable trading|\n|Market|String|\"BTC\"|Market to trade on - BTC, ETH, USDT, etc.|\n|Exchange|String|\"Binance\"|Exchange to trade on|\n|MaxPairs|Number|16|Maximum pairs to trade with|\n|MinCost|Number|0.000999|Ignore pairs with the specified market value or lower (dust)|\n|ExcludedPairs|Array|[ \"BNBBTC\" ]|Pairs excluded from trading|\n|TradePriceType|String|\"Last\"|Price type to use for trading. Available values: Last, Ask, Bid|\n\n###### Buying\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|BuyEnabled|Boolean|true|Enable / disable buying|\n|BuyType|String|\"Market\"|Supported types: Market. Limit is not currently supported|\n|BuyMaxCost|Number|0.0012|Maximum cost when buying a new pair|\n|BuyMultiplier|Number|1|Maximum cost multiplier|\n|BuyMinBalance|Number|0|Minimum account balance to buy new pairs|\n|BuySamePairTimeout|Number|900|Rebuy same pair timeout (in seconds)|\n|BuyTrailing|Number|-0.15|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)|\n|BuyTrailingStopMargin|Number|0.05|Stop trailing and place buy order immediately when margin hits the specified value|\n|BuyTrailingStopAction|String|\"Buy\"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel|\n\n###### Buying DCA\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|BuyDCAEnabled|Boolean|true|Enable / disable buying DCA|\n|BuyDCAMultiplier|Number|1|Current cost multiplier. To double down set to 1|\n|BuyDCAMinBalance|Number|0|Minimum account balance to DCA|\n|BuyDCASamePairTimeout|Number|4200|Rebuy same pair timeout (in seconds)|\n|BuyDCATrailing|Number|-1.50|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)|\n|BuyDCATrailingStopMargin|Number|0.40|Stop trailing and place buy order immediately when margin hits the specified value|\n|BuyDCATrailingStopAction|String|\"Cancel\"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel|\n\n###### Selling\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|SellEnabled|Boolean|true|Enable / disable selling|\n|SellType|String|\"Market\"|Supported types: Market. Limit is not currently supported|\n|SellMargin|Number|2.00|Minimum percentage increase to start trailing|\n|SellTrailing|Number|0.70|Sell trailing percentage (should be a positive number, set to 0 to disable trailing)|\n|SellTrailingStopMargin|Number|1.70|Stop trailing and place sell order immediately when margin hits the specified value|\n|SellTrailingStopAction|String|\"Sell\"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel|\n|SellStopLossEnabled|Boolean|false|Enable / disable stop loss trigger|\n|SellStopLossAfterDCA|Boolean|true|Trigger stop loss only after all DCA levels has been reached|\n|SellStopLossMinAge|Number|3.0|Minimum number of days needed before triggering the stop loss|\n|SellStopLossMargin|Number|-20|Trigger stop loss and immediately place sell order at the specified percentage decrease|\n\n###### Selling DCA\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|SellDCAMargin|Number|1.50|Minimum percentage increase to start trailing|\n|SellDCATrailing|Number|0.50|Sell trailing percentage (set to 0 to disable trailing)\n|SellDCATrailingStopMargin|Number|1.25|Stop trailing and place sell order immediately when margin hits the specified value|\n|SellDCATrailingStopAction|String|\"Sell\"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel|\n|RepeatLastDCALevel|Boolean|fasle|Repeat the last DCA Level indefinitely, essentially making the DCA level number unlimited|\n|DCALevels|Array|[DCA Levels](#Dca_Levels)|Action to take after hitting the StopMargin. Possible values: Sell, Cancel|\n\n###### DCA Levels\n\nRead more about how DCA works in the [DCA](#Dca) section.\n\nDCALevels setting is an array of DCA levels. There is no limit to the number of levels. The only mandatory setting for each level is Margin, which specifies when to trigger the DCA. All other settings are optional and when omitted will use the default values as defined above.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Margin|Number|-1.50|When to trigger the DCA|\n|BuyMultiplier|Number|BuyDCAMultiplier|Current cost multiplier. To double down set to 1 (Optional)|\n|BuySamePairTimeout|Number|BuyDCASamePairTimeout|Rebuy same pair timeout (in seconds) (Optional)|\n|BuyTrailing|Number|BuyDCATrailing|Buy trailing percentage (should be a negative number, set to 0 to disable trailing) (Optional)|\n|BuyTrailingStopMargin|Number|BuyDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)|\n|BuyTrailingStopAction|String|BuyDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Buy, Cancel (Optional)|\n|SellMargin|Number|SellDCAMargin|Minimum percentage increase to start trailing (Optional)|\n|SellTrailing|Number|SellDCATrailing|Sell trailing percentage (set to 0 to disable trailing) (Optional)|\n|SellTrailingStopMargin|Number|SellDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)|\n|SellTrailingStopAction|String|SellDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Sell, Cancel (Optional)|\n\n###### Accounts\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|TradingCheckInterval|Number|1|Tickers check frequency (in seconds)|\n|AccountRefreshInterval|Number|360|Exchange account refresh interval (in seconds)|\n|AccountInitialBalance|Number|0.12|Initial balance on the account, used for stats calculations|\n|AccountInitialBalanceDate|String|\"2018-04-08T00:00:00+00:00\"|Date of the initial balance snapshot|\n|AccountFilePath|String|\"data/exchange-account.json\"|Path to the account file|\n|VirtualTrading|Boolean|true|Enable / disable virtual trading|\n|VirtualTradingFees|Number|0.0005|Trading fees (percentage)|\n|VirtualAccountInitialBalance|Number|0.12|Initial balance on the virtual account, used for stats calculations|\n|VirtualAccountFilePath|String|\"data/virtual-account.json\"|Path to the virtual account file|\n\n#### Rules Configuration\n\nRead more about how rules work in the [rules](#Rules) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Modules|Array|[Module](#Module)|Rule Modules|\n\n###### Module\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Module|String|\"Module\"|Module name|\n|Configuration|Object|[Module Configuration](#Module_Configuration)|Module configuration|\n|Entries|Array|[Rules Configuration](#Rules_Configuration)|Rules configuration|\n\n###### Module Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|ProcessingMode|String|\"AllMatches\"|Available modes: AllMatches (all enabled rules will get processed) or FirstMatch (stop processing at the first rule that is satisfied)|\n|CheckInterval|Number|3|Rules check frequency (in seconds)|\n\n###### Rules Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enaled|Boolean|true|Enable / disable rule|\n|Name|String|Default|Rule name|\n|Modifiers|Object|[Rule Modifiers](#Rule_Modifiers)|Rule modifiers|\n|Conditions|Array|[Rule Conditions](#Rule_Conditions)|Rule conditions|\n|Trailing|Object|[Rule trailing](#Rule_Trailing)|Rule trailing|\n\n###### Rule Modifiers\n\nAll modifiers (both signal and trading) are optional and can be omitted.  \nSignal modifiers currently only support the *CostMultiplier* (MaxCost multiplier)  \nTrading modifiers support any trading setting that begins with Buy, Sell plus DCALevels and MaxPairs in addition to the following trading-rule specific modifiers:\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|SwapEnabled|Boolean|true|Swap badly performing pairs for better performing ones|\n|SwapSignalRules|Array|[ \"Swap\" ]|Rules used to buy the replacement pair with|\n|SwapTimeout|Number|10800|How long to wait before making a swap (in seconds)|\n|ArbitrageEnabled|Boolean|false|Enable arbitrage for that pair|\n|ArbitrageMarkets|Array|[ \"ETH\" ]|Markets to use for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be used|\n|ArbitrageType|String|\"Reverse\"|Type of arbitrage to use. Available values: Direct, Reverse. When omitted both types will be used|\n|ArbitrageBuyMultiplier|Number|0.99|Percentage of the arbitrage pair to buy|\n|ArbitrageSellMultiplier|Number|0.99|Percentage of the arbitrage pair to sell|\n|ArgbitrageSignalRules|Array|[ \"Arbitrage\" ]|Rules used to arbitrage pairs|\n\n###### Rule Conditions\n\nRule is considered satisfied when all the conditions are met. When trailing is enabled, its conditions must be met first.  \nYou can have multiple sets of conditions within a single rule.  \nIs is not necessary to specify a Signal if none of the conditions are signal-specific.\n\n|Setting|Type|Signal Specific|Description|\n|-|:-:|:-:|-|\n|Signal|String|N/A|Signal with which to calculate signal-specific conditions|\n|MinGlobalRating|Number|No|Minimal global rating calculated from all individual coins ratings. Is a value between -1 and 1. Value -0.2 is considered a bearish market|\n|MaxGlobalRating|Number|No|Maximum global rating calculated from all individual coins ratings. Is a value between -1 and 1. Value +0.2 is considered a bullish market|\n|MinRating|Number|Yes|Minimal rating of a coin within the specified signal's period. Expect a value between -1 and 1. A good place to start is 0.3+|\n|MaxRating|Number|Yes|Maximal ration of a coin within the specified signal's period. Expect a value between -1 and 1. In a normal market do not expect it to go over 0.6|\n|MinRatingChange|Number|Yes|The minimal rate of change of coins rating (percentage). For a reference values let the bot run and then have a look in the dashboard|\n|MaxRatingChange|Number|Yes|The maximal rate of change of coins rating (percentage). For a reference values let the bot run and then have a look in the dashboard|\n|MinPrice|Number|No|Minimum current price of the coin|\n|MaxPrice|Number|No|Maximum current price of the coin|\n|MinPriceChange|Number|Yes|The minimal rate of change of price withing specified period frame (percentage)|\n|MaxPriceChange|Number|Yes|The maximal rate of change of price withing specified period frame (percentage)|\n|MinSpread|Number|No|Minimum difference between current bid and ask price (percentage)|\n|MaxSpread|Number|No|Maximum difference between current bid and ask price (percentage)|\n|MinVolume|Number|Yes|Minimum coin volume within the specified signal's period. Do not expect 24h volume in this category|\n|MaxVolume|Number|Yes|Maximum coin volume within the specified signal's period. Do not expect 24h volume in this category|\n|MinVolumeChange|Number|Yes|The minimal rate of change of volume withing specified period frame (percentage)|\n|MaxVolumeChange|Number|Yes|The maximal rate of change of volume withing specified period frame (percentage)|\n|MinVolatility|Number|Yes|Minimum average volatility of a coin within its own specified timeframe|\n|MaxVolatility|Number|Yes|Maximum average volatility of a coin within its own specified timeframe|\n|MinAge|Number|No|Minimum trading pair's age (in days, e.g. 1.5 is 36 hours)|\n|MaxAge|Number|No|Maximum trading pair's age (in days, e.g. 1.5 is 36 hours)|\n|MinLastBuyAge|Number|No|Minimum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)|\n|MaxLastBuyAge|Number|No|Maximum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)|\n|MinMargin|Number|No|Minimum trading pair's margin|\n|MaxMargin|Number|No|Maximum trading pair's margin|\n|MinMarginChange|Number|No|Minimum trading pair's margin change since last buy|\n|MaxMarginChange|Number|No|Maximum trading pair's margin change since last buy|\n|MinAmount|Number|No|Minimum trading pair's total purchase amount|\n|MaxAmount|Number|No|Maximum trading pair's total purchase amount|\n|MinCost|Number|No|Minimum trading pair's total current cost|\n|MaxCost|Number|No|Maximum trading pair's total current cost|\n|MinDCALevel|Number|No|Minimum trading pair's DCA level|\n|MaxDCALevel|Number|No|Maximum trading pair's DCA level|\n|MinArbitrage|Number|No|Minimum triangular arbitrage value|\n|MaxArbitrage|Number|No|Maximum triangular arbitrage value|\n|ArbitrageMarket|String|No|Market to look for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be considered|\n|ArbitrageType|String|No|Type of arbitrage to look for. Available values: Direct, Reverse. When omitted both types will be considered|\n|Pairs|Array|No|List of pairs to directly apply the rule to|\n|NotPairs|Array|No|List of pairs to not apply the rule to|\n|SignalRules|Array|No|List of signal rules that were used to buy a pair|\n|NotSignalRules|Array|No|List of signal rules that were not used to buy a pair|\n\n###### Rule Trailing\n\nTrailing is optional and is only supported by the Signal Rules.   \nTrailing starts when all the Rule Trailing StartConditions are met.\nTrailing ends when all the conditions of the rule are met, at any point between MinDuration and MaxDuration.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable trailing|\n|MinDuration|Number|25|Minimum trail duration (in seconds)|\n|MaxDuration|Number|240|Maximum trail duration (in seconds)|\n|StartConditions|Array|[Rule Conditions](#Rule_Conditions)|Begin trailing when all the below conditions are met.|\n\n#### Notification Configuration\n\nRead more about how nofitications work in the [notifications](#Notifications) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|false|Enable / disable notifications|\n|TelegramEnabled|Boolean|true|Enabled / disable Telegram notifications|\n|TelegramBotToken|String|-|Your Telegram bot's token|\n|TelegramChatId|Number|-|Your Telegram chat id|\n|TelegramAlertsEnabled|Boolean|true|Enable phone alerts with Telegram messages|\n\n#### Backtesting Configuration\n\nRead more about how backtesting works in the [backtesting](#Backtesting) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|false|Enable / disable backtesting|\n|Replay|Boolean|false|Enabled / disable snapshots replay|\n|ReplayOutput|Boolean|true|Display replay output in the log|\n|ReplaySpeed|Number|500|Replay speed (1 = SnapshotInterval speed)|\n|ReplayStartIndex|Number|null|Snapshot index to start replay with|\n|ReplayEndIndex|Number|null|Snapshot index to end replay with|\n|DeleteLogs|Boolean|false|Delete all existing logs before running backtesting|\n|DeleteAccountData|Boolean|false|Delete account data before running backtesting|\n|CopyAccountDataPath|String|null|Path to copy existing account data file from for backtesting|\n|TradingSpeedEasing|Number|0|Slow down the trading service while replaying snapshots|\n|TradingRulesSpeedEasing|Number|0|Slow down the trading rules service while replaying snapshots|\n|SignalRulesSpeedEasing|Number|0|Slow down the signal rules service while replaying snapshots|\n|SnapshotsInterval|Number|1|How often to take snapshots (in seconds)|\n|SnapshotsPath|String|data/backtesting|Where to save the new snapshots or to load existing ones when replaying|\n\n#### Other Configuration\n\n###### Exchange\n\nExchange-specific configuration. Don't change the default values unless you know what you're doing.\n\n###### Logging\n\nLogging configuration. Here you can disable logging completely, change the default log file paths, verbosity and format.\n\n###### Paths\n\nPaths to all the configuration files. Do not change unless you know what you're doing.\n\n###### Caching\n\nCurrently not in use.\n\n###### Integration\n\nCurrently not in use.\n\nTrading\n--------------\n\nThere are two ways you can trade with IntelliTrader. Virtual trading is enabled on by default.  \nDO NOT switch to live trading until you are fully confident with the bot and are familiar with the way it does trading.  \nConceptually there is no difference between virtual and live trading, so virtual trading is a very good way to learn the ins and outs, experiment, and try out new settings.\n\n#### Virtual Trading\n\nVirtual trading is enabled by default. You don't need to provide your API Key to virtual trade.  \nTo enable virtual trading, set *VirtualTrading* to true in config/trading.json. \nYou might want to change *VirtualAccountInitialBalance* to reflect your starting balance for testing. \n\n#### Live Trading\n\nTo trade on an exchange, first you need to create an encrypted file that will hold your API keys. \nOpen data/encrypt-keys and change public_key to your API key and private_key to your API secret, then run it.  \nYou should now have the generated keys.bin file in your IntelliTrader directory. This file contains your encrypted API keys and it is only valid for the current user and only on the computer it is created on.  \nImportant: Make sure to remove your keys from data/encrypt-keys file after generating the keys.bin. Now change *VirtualTrading* to false in config/trading.json. \nAlso, to make the profit stats accurate, you need to set *AccountInitialBalance* to your current BTC/ETH balance (depending on the market you use). \nThat's it, you are ready for exchange trading!\n\n#### Trailing\n\nWhen all the buy conditions are true, then the bot is in its final phase where it tries to find the bottom with the help of trailing. The bot is watching the price of a coin closely. The price needs to fall and then rise by at least the percentage specified for trailing in order to make a buy.\n\nExample trailing story for value -1 (percent):\n\n*The coin keeps falling more than 3 percent. It then rises by 0.7 percent. This move is smaller than 1 percent, meaning the bot does nothing and the trailing continues. After another drop the coin jumps 1.5 percent, so the bot will buy because the trailing has exceeded our buy value.*\n\n#### Signals\n\nSignals are used to buy new pairs based on the predefined rules.\n\nRead more about how signal rules work in the [signal rules](#Signal_Rules) section.\n\n#### Global Rating\n\nAn average value of all the ratings combined (for current market & exchange)\n\n#### DCA\n\nBuy additional position at a lower price than the original purchase price. This brings the average price you've paid the pair down.\n\n#### Stop Loss\n\nSell a pair at a specified negative margin to avoid it dipping even lower.\n\n#### Pair Swapping\n\nSwap badly performing pairs for better performing ones.\nYou would need at least one signal rule and one trading rule to enable this feature. The trading rule will enable swapping feature for the specific pairs (e.g. when the pair is old, has low margin, low rating, etc.) and the signal rule will determine the conditions for which pairs to buy instead of the swapped ones.\n\n#### Arbitrage\n\n*TODO: Add detailed explanation here*\n\n#### Backtesting\n\nBacktesting works by taking snapshots of signals and exchange tickers over a period of time (the longer the period the better) and then replaing them at high speed to the bot. Essentially what this means is that you could snapshot a week's worth of data and then replay it in 10 minutes, saving yourself a lot of time. Snapshots can be reused for as many times as you like and with any settings you wish.\n\nRules\n-------------\n\n#### Signal Rules\n\nSignal Rules are used to buy new pairs based on the specific conditions. This can also optionally have trailing.\nIf enabled then Signal Trailing in IntelliTrader is the first stage of buying where it will trail a coin based on the settings you specify for the time duration's you specify. If a coin matches the trailing conditions it then checks the conditions of the coin and buys the coin if these are met. \nIf trailing isn't enabled it buys based only on the specific conditions set.\n\nAdd \"Action\": \"Swap\" if you want the signal rule to only be used for pair swapping.  \nAdd \"Action\": \"Arbitrage\" if you want the signal rule to only be used for arbitrage.\n\n#### Trading Rules\n\nTrading Rules apply to Pairs you already hold. Trailing is not available for trading rules. Conditions for Trading rules work the same way as in signal rules.\nAvailable modifiers for Trading Rules are any trading.json setting that begins with Buy, Sell or DCALevels.\nThis is also where you would setup your Swap Specific Rules.\n\nWeb Interface\n-------------\n\nTo access the web interface, simply open http://localhost:7000 in your browser (or replace 7000 with the port you have configured).\n\n#### Status Bar\n\nStatus bar contains information about your available balance, current global rating, trailing buys/sells/signals and the current status. Hover over the status icon (ON) to see the latest health check information.\n\n#### Dashboard\n\nThis is the default screen that IntelliTrader starts at. The table shows all the pairs that you currently hold. The data automatically refreshes every 5 seconds.\n\nColumns can be adjusted. You can reorder them, move them around and even remove the ones that you don't need. There are some options for exporting the data: you can copy or export it to a spreadsheet.\n\nYou can click on the table rows to expand them and access additional options. From there you can manually buy, sell or view the current pair's settings. \n\nThe log button will display the last 5 lines from the log file.\n\n|Column|Description|\n|-|-|\n|More|Displayed on low resolution screens and when clicked shows additional data|\n|Pair #1|Pair's name|\n|Pair #2|Pair's name, including the current DCA level|\n|DCA|Current DCA level|\n|Margin|Current profit percentage. If you sold right now, this is what you would make, approximately|\n|Target|Target sell point in profit. Also known as the profit margin|\n|Rating|Pair's current rating based on the signals source, Green means it is currently higher than the purchase rating|\n|Rating Bought|Rating that the pair was purchased at|\n|Age|How long a pair has been held for|\n|Amount|Total amount of the pair you currently hold|\n|Cost|Current value of the pair|\n|Cost Bought|Purchase value of the pair|\n|Price|Current price of the pair on the exchange|\n|Price Bought|Price paid on purchase|\n|Spread|Difference between current bid and ask price|\n|Signal Rule|Signal rule used to buy the pair|\n|Trading Rules|Trading rules currently applied to the pair|\n|Order Dates|The dates of the purchase orders|\n|Order IDs|The order Ids on the exchange|\n\nAlong the bottom of the main area you will find useful information regarding the current pairs, Total Pairs, Average Margin, Average Rating, Average Age, and Total Cost of all pairs together.\n\n#### Market\n\nThis is the current market information page. The table shows all the pairs that are currently available on the exchange along with a variety of data. The data automatically refreshes every 5 seconds.\n\nColumns can be adjust. You can reorder them, move them around and even remove the ones that you don't need. There are some options for exporting the data: you can copy or export it to a spreadsheet.\n\nYou can click on the table rows to expand them and access additional options. From there you can manually buy, buy default (buys at the max cost) or view the current pair's settings. \n\nThe log button will display the last 5 lines from the log file.\n\n|Column|Description|\n|-|-|\n|More|Displayed on low resolution screens and when clicked shows additional data|\n|Name|Pair's name|\n|Rating|Ratings for each signal|\n|% Rating Change|Percentage rating change for each signal|\n|Price|Current pair's price (same for every signal)|\n|% Price Change|Percentage price change for each signal|\n|Spread|Difference between current bid and ask price|\n|Arbitrage|Triangular arbitrage values for every available market|\n|Volume|Volume for each signal|\n|% Volume Change|Percentage volume change for each signal|\n|Volatility|Volatility for each signal|\n|Trading Rules|Currently applied trading rules|\n|Signal Rules|Currently applied signal rules|\n\n#### Stats\n\nHere you can see your total overall profit and current account balance.\nThere is also a breakdown of profits by day, along with other information, like average margin.\n\nYou can click on the number of trades to see information about the orders completed on that particular day.  \n\nYou can also open the Rules Analyzer page to check how each rule is performing.\n\n#### Settings\n\nHere you can temporarily enable or disable a small subset of bot options.  \n\nIf you would like to make permanent changes to settings, you can use the Advanced panel. Saving the changes there will take immediate and permanent effect. In the advanced editor you can switch between different editing modes (Tree, Code, Form, Text, View).\n\nThere are also three options available on that page:\n- Restart Services: this will restarts all the bot's services (core, trading, signals, rules, etc.). Useful when something went wrong and you don't have access to your machine / VPS.\n- Refresh Account: you could use this option if you have made a manual trade on an exchange and would like to update your account immediately. Hoewever, remember that there is a period account refresh (every 6 minutes by default), so this is rarely necessary. \n- Log Out: log out of IntelliTrader\n\n#### Log\n\nDisplays the last 500 lines from the log file.\n\n#### Help\n\nThis page\n\nNotifications\n-------------\n\n#### Telegram\n\nIn the config/notification.json change *Enabled* to true, set *TelegramBotToken* to your bot's token and *TelegramChatId* to your chat id to enable Telegram notifications. Set *TelegramAlertsEnabled* to *false* if you don't want to receive alerts with notifications.  \n\nTalk to @botfather to create a new bot and then talk to @FalconGate_Bot to get your telegram chat id (type /get\\_my\\_id).\n\nHealth Checks\n-------------\n\nHealth check is a periodic test of all the bot's services to make sure that every single one of them is running smoothly and with no interruptions. Trading gets suspended if at least one of the health checks fails until it passes again. If notifications are enabled, you will get a notification for both occasions.\n\nTroubleshooting\n-------------\n\n#### IntelliTrader is not starting or crashing on startup\nPlease made sure that your system meets all the requirements and that you have all the prerequisites. Also check if there are any errors in the log/*-general.log file.\n\n#### IntelliTrader is not trading or is behaving weirdly in general\nAgain, check the log file for any errors, it is very likely that there will be a clue there.\n\nLicense & Disclaimer\n-------------\n\nBy using, or simply downloading IntelliTrader (the Software), you understand and accept the following:\n\n\nLicensing\n\nThe Software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International licence. Full terms of the licence can be found by following this link:\n\nhttps://creativecommons.org/licenses/by-nc-sa/4.0/legalcode\n\nA full copy of the license is also included with the download.\n\n\nLimitation Of Liability\n\nIn no event shall liability be accepted for any indirect, incidental, special, consequential or punitive damages, including, without limitation, loss of profits, funds, data, use, goodwill, or other tangible and intangible losses, resulting from:\n\n\t(i) your access to, or use of, or inability to access or use the Software; \n\t(ii) any content of any third party used with the Software; \n\t(iii) any content obtained from the Software; and \n\t(iv) unauthorized access, use or alteration of your transmissions or content, whether based on warranty, contract, tort (including negligence) or any other legal theory, whether or not we have been informed of the possibility of such damage, and even if a remedy set forth herein is found to have failed of its essential purpose.\n\nWe do not refund losses.\n\n\nDisclaimer\n\nYour use of the Software is at your sole risk. The Software is provided on an AS IS and AS AVAILABLE basis. The Software is provided without warranties of any kind, whether expressed or implied, including, but not limited to, implied warranties of merchantability, fitness for a particular purpose, non-infringement or course of performance.\n\nNo warranty of any kind is expressed or implied, that:\n\n\ta) the Software will function, be secure or operate on any nominated platform; \n\tb) any errors or defects will be corrected; \n\tc) the Software is free of viruses or other harmful components; or \n\td) the results of using the Software will meet your requirements.\n\n\nIf you do not agree with any of the above, please do not download or use the Software."
  },
  {
    "path": "IntelliTrader.Web/Static/Help/navigation.md",
    "content": ""
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/Vendor/mdwiki.js",
    "content": "﻿(function () {\n\n    /**\n     * Block-Level Grammar\n     */\n\n    var block = {\n        newline: /^\\n+/,\n        code: /^( {4}[^\\n]+\\n*)+/,\n        fences: noop,\n        hr: /^( *[-*_]){3,} *(?:\\n+|$)/,\n        heading: /^ *(#{1,6}) *([^\\n]+?) *#* *(?:\\n+|$)/,\n        nptable: noop,\n        lheading: /^([^\\n]+)\\n *(=|-){3,} *\\n*/,\n        blockquote: /^( *>[^\\n]+(\\n[^\\n]+)*\\n*)+/,\n        list: /^( *)(bull) [\\s\\S]+?(?:hr|\\n{2,}(?! )(?!\\1bull )\\n*|\\s*$)/,\n        html: /^ *(?:comment|closed|closing) *(?:\\n{2,}|\\s*$)/,\n        def: /^ *\\[([^\\]]+)\\]: *<?([^\\s>]+)>?(?: +[\"(]([^\\n]+)[\")])? *(?:\\n+|$)/,\n        table: noop,\n        paragraph: /^((?:[^\\n]+\\n?(?!hr|heading|lheading|blockquote|tag|def))+)\\n*/,\n        text: /^[^\\n]+/\n    };\n\n    block.bullet = /(?:[*+-]|\\d+\\.)/;\n    block.item = /^( *)(bull) [^\\n]*(?:\\n(?!\\1bull )[^\\n]*)*/;\n    block.item = replace(block.item, 'gm')\n        (/bull/g, block.bullet)\n        ();\n\n    block.list = replace(block.list)\n        (/bull/g, block.bullet)\n        ('hr', /\\n+(?=(?: *[-*_]){3,} *(?:\\n+|$))/)\n        ();\n\n    block._tag = '(?!(?:'\n        + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'\n        + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'\n        + '|span|br|wbr|ins|del|img)\\\\b)\\\\w+(?!:/|@)\\\\b';\n\n    block.html = replace(block.html)\n        ('comment', /<!--[\\s\\S]*?-->/)\n        ('closed', /<(tag)[\\s\\S]+?<\\/\\1>/)\n        ('closing', /<tag(?:\"[^\"]*\"|'[^']*'|[^'\">])*?>/)\n        (/tag/g, block._tag)\n        ();\n\n    block.paragraph = replace(block.paragraph)\n        ('hr', block.hr)\n        ('heading', block.heading)\n        ('lheading', block.lheading)\n        ('blockquote', block.blockquote)\n        ('tag', '<' + block._tag)\n        ('def', block.def)\n        ();\n\n    /**\n     * Normal Block Grammar\n     */\n\n    block.normal = merge({}, block);\n\n    /**\n     * GFM Block Grammar\n     */\n\n    block.gfm = merge({}, block.normal, {\n        fences: /^ *(`{3,}|~{3,}) *(\\S+)? *\\n([\\s\\S]+?)\\s*\\1 *(?:\\n+|$)/,\n        paragraph: /^/\n    });\n\n    block.gfm.paragraph = replace(block.paragraph)\n        ('(?!', '(?!' + block.gfm.fences.source.replace('\\\\1', '\\\\2') + '|')\n        ();\n\n    /**\n     * GFM + Tables Block Grammar\n     */\n\n    block.tables = merge({}, block.gfm, {\n        nptable: /^ *(\\S.*\\|.*)\\n *([-:]+ *\\|[-| :]*)\\n((?:.*\\|.*(?:\\n|$))*)\\n*/,\n        table: /^ *\\|(.+)\\n *\\|( *[-:]+[-| :]*)\\n((?: *\\|.*(?:\\n|$))*)\\n*/\n    });\n\n    /**\n     * Block Lexer\n     */\n\n    function Lexer(options) {\n        this.tokens = [];\n        this.tokens.links = {};\n        this.options = options || marked.defaults;\n        this.rules = block.normal;\n\n        if (this.options.gfm) {\n            if (this.options.tables) {\n                this.rules = block.tables;\n            } else {\n                this.rules = block.gfm;\n            }\n        }\n    }\n\n    /**\n     * Expose Block Rules\n     */\n\n    Lexer.rules = block;\n\n    /**\n     * Static Lex Method\n     */\n\n    Lexer.lex = function (src, options) {\n        var lexer = new Lexer(options);\n        return lexer.lex(src);\n    };\n\n    /**\n     * Preprocessing\n     */\n\n    Lexer.prototype.lex = function (src) {\n        src = src\n            .replace(/\\r\\n|\\r/g, '\\n')\n            .replace(/\\t/g, '    ')\n            .replace(/\\u00a0/g, ' ')\n            .replace(/\\u2424/g, '\\n');\n\n        return this.token(src, true);\n    };\n\n    /**\n     * Lexing\n     */\n\n    Lexer.prototype.token = function (src, top) {\n        var src = src.replace(/^ +$/gm, '')\n            , next\n            , loose\n            , cap\n            , bull\n            , b\n            , item\n            , space\n            , i\n            , l;\n\n        while (src) {\n            // newline\n            if (cap = this.rules.newline.exec(src)) {\n                src = src.substring(cap[0].length);\n                if (cap[0].length > 1) {\n                    this.tokens.push({\n                        type: 'space'\n                    });\n                }\n            }\n\n            // code\n            if (cap = this.rules.code.exec(src)) {\n                src = src.substring(cap[0].length);\n                cap = cap[0].replace(/^ {4}/gm, '');\n                this.tokens.push({\n                    type: 'code',\n                    text: !this.options.pedantic\n                        ? cap.replace(/\\n+$/, '')\n                        : cap\n                });\n                continue;\n            }\n\n            // fences (gfm)\n            if (cap = this.rules.fences.exec(src)) {\n                src = src.substring(cap[0].length);\n                this.tokens.push({\n                    type: 'code',\n                    lang: cap[2],\n                    text: cap[3]\n                });\n                continue;\n            }\n\n            // heading\n            if (cap = this.rules.heading.exec(src)) {\n                src = src.substring(cap[0].length);\n                this.tokens.push({\n                    type: 'heading',\n                    depth: cap[1].length,\n                    text: cap[2]\n                });\n                continue;\n            }\n\n            // table no leading pipe (gfm)\n            if (top && (cap = this.rules.nptable.exec(src))) {\n                src = src.substring(cap[0].length);\n\n                item = {\n                    type: 'table',\n                    header: cap[1].replace(/^ *| *\\| *$/g, '').split(/ *\\| */),\n                    align: cap[2].replace(/^ *|\\| *$/g, '').split(/ *\\| */),\n                    cells: cap[3].replace(/\\n$/, '').split('\\n')\n                };\n\n                for (i = 0; i < item.align.length; i++) {\n                    if (/^ *-+: *$/.test(item.align[i])) {\n                        item.align[i] = 'right';\n                    } else if (/^ *:-+: *$/.test(item.align[i])) {\n                        item.align[i] = 'center';\n                    } else if (/^ *:-+ *$/.test(item.align[i])) {\n                        item.align[i] = 'left';\n                    } else {\n                        item.align[i] = null;\n                    }\n                }\n\n                for (i = 0; i < item.cells.length; i++) {\n                    item.cells[i] = item.cells[i].split(/ *\\| */);\n                }\n\n                this.tokens.push(item);\n\n                continue;\n            }\n\n            // lheading\n            if (cap = this.rules.lheading.exec(src)) {\n                src = src.substring(cap[0].length);\n                this.tokens.push({\n                    type: 'heading',\n                    depth: cap[2] === '=' ? 1 : 2,\n                    text: cap[1]\n                });\n                continue;\n            }\n\n            // hr\n            if (cap = this.rules.hr.exec(src)) {\n                src = src.substring(cap[0].length);\n                this.tokens.push({\n                    type: 'hr'\n                });\n                continue;\n            }\n\n            // blockquote\n            if (cap = this.rules.blockquote.exec(src)) {\n                src = src.substring(cap[0].length);\n\n                this.tokens.push({\n                    type: 'blockquote_start'\n                });\n\n                cap = cap[0].replace(/^ *> ?/gm, '');\n\n                // Pass `top` to keep the current\n                // \"toplevel\" state. This is exactly\n                // how markdown.pl works.\n                this.token(cap, top);\n\n                this.tokens.push({\n                    type: 'blockquote_end'\n                });\n\n                continue;\n            }\n\n            // list\n            if (cap = this.rules.list.exec(src)) {\n                src = src.substring(cap[0].length);\n                bull = cap[2];\n\n                this.tokens.push({\n                    type: 'list_start',\n                    ordered: bull.length > 1\n                });\n\n                // Get each top-level item.\n                cap = cap[0].match(this.rules.item);\n\n                next = false;\n                l = cap.length;\n                i = 0;\n\n                for (; i < l; i++) {\n                    item = cap[i];\n\n                    // Remove the list item's bullet\n                    // so it is seen as the next token.\n                    space = item.length;\n                    item = item.replace(/^ *([*+-]|\\d+\\.) +/, '');\n\n                    // Outdent whatever the\n                    // list item contains. Hacky.\n                    if (~item.indexOf('\\n ')) {\n                        space -= item.length;\n                        item = !this.options.pedantic\n                            ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')\n                            : item.replace(/^ {1,4}/gm, '');\n                    }\n\n                    // Determine whether the next list item belongs here.\n                    // Backpedal if it does not belong in this list.\n                    if (this.options.smartLists && i !== l - 1) {\n                        b = block.bullet.exec(cap[i + 1])[0];\n                        if (bull !== b && !(bull.length > 1 && b.length > 1)) {\n                            src = cap.slice(i + 1).join('\\n') + src;\n                            i = l - 1;\n                        }\n                    }\n\n                    // Determine whether item is loose or not.\n                    // Use: /(^|\\n)(?! )[^\\n]+\\n\\n(?!\\s*$)/\n                    // for discount behavior.\n                    loose = next || /\\n\\n(?!\\s*$)/.test(item);\n                    if (i !== l - 1) {\n                        next = item[item.length - 1] === '\\n';\n                        if (!loose) loose = next;\n                    }\n\n                    this.tokens.push({\n                        type: loose\n                            ? 'loose_item_start'\n                            : 'list_item_start'\n                    });\n\n                    // Recurse.\n                    this.token(item, false);\n\n                    this.tokens.push({\n                        type: 'list_item_end'\n                    });\n                }\n\n                this.tokens.push({\n                    type: 'list_end'\n                });\n\n                continue;\n            }\n\n            // html\n            if (cap = this.rules.html.exec(src)) {\n                src = src.substring(cap[0].length);\n                this.tokens.push({\n                    type: this.options.sanitize\n                        ? 'paragraph'\n                        : 'html',\n                    pre: cap[1] === 'pre' || cap[1] === 'script',\n                    text: cap[0]\n                });\n                continue;\n            }\n\n            // def\n            if (top && (cap = this.rules.def.exec(src))) {\n                src = src.substring(cap[0].length);\n                this.tokens.links[cap[1].toLowerCase()] = {\n                    href: cap[2],\n                    title: cap[3]\n                };\n                continue;\n            }\n\n            // table (gfm)\n            if (top && (cap = this.rules.table.exec(src))) {\n                src = src.substring(cap[0].length);\n\n                item = {\n                    type: 'table',\n                    header: cap[1].replace(/^ *| *\\| *$/g, '').split(/ *\\| */),\n                    align: cap[2].replace(/^ *|\\| *$/g, '').split(/ *\\| */),\n                    cells: cap[3].replace(/(?: *\\| *)?\\n$/, '').split('\\n')\n                };\n\n                for (i = 0; i < item.align.length; i++) {\n                    if (/^ *-+: *$/.test(item.align[i])) {\n                        item.align[i] = 'right';\n                    } else if (/^ *:-+: *$/.test(item.align[i])) {\n                        item.align[i] = 'center';\n                    } else if (/^ *:-+ *$/.test(item.align[i])) {\n                        item.align[i] = 'left';\n                    } else {\n                        item.align[i] = null;\n                    }\n                }\n\n                for (i = 0; i < item.cells.length; i++) {\n                    item.cells[i] = item.cells[i]\n                        .replace(/^ *\\| *| *\\| *$/g, '')\n                        .split(/ *\\| */);\n                }\n\n                this.tokens.push(item);\n\n                continue;\n            }\n\n            // top-level paragraph\n            if (top && (cap = this.rules.paragraph.exec(src))) {\n                src = src.substring(cap[0].length);\n                this.tokens.push({\n                    type: 'paragraph',\n                    text: cap[1][cap[1].length - 1] === '\\n'\n                        ? cap[1].slice(0, -1)\n                        : cap[1]\n                });\n                continue;\n            }\n\n            // text\n            if (cap = this.rules.text.exec(src)) {\n                // Top-level should never reach here.\n                src = src.substring(cap[0].length);\n                this.tokens.push({\n                    type: 'text',\n                    text: cap[0]\n                });\n                continue;\n            }\n\n            if (src) {\n                throw new\n                    Error('Infinite loop on byte: ' + src.charCodeAt(0));\n            }\n        }\n\n        return this.tokens;\n    };\n\n    /**\n     * Inline-Level Grammar\n     */\n\n    var inline = {\n        escape: /^\\\\([\\\\`*{}\\[\\]()#+\\-.!_>])/,\n        autolink: /^<([^ >]+(@|:\\/)[^ >]+)>/,\n        url: noop,\n        tag: /^<!--[\\s\\S]*?-->|^<\\/?\\w+(?:\"[^\"]*\"|'[^']*'|[^'\">])*?>/,\n        link: /^!?\\[(inside)\\]\\(href\\)/,\n        reflink: /^!?\\[(inside)\\]\\s*\\[([^\\]]*)\\]/,\n        nolink: /^!?\\[((?:\\[[^\\]]*\\]|[^\\[\\]])*)\\]/,\n        strong: /^__([\\s\\S]+?)__(?!_)|^\\*\\*([\\s\\S]+?)\\*\\*(?!\\*)/,\n        em: /^\\b_((?:__|[\\s\\S])+?)_\\b|^\\*((?:\\*\\*|[\\s\\S])+?)\\*(?!\\*)/,\n        code: /^(`+)\\s*([\\s\\S]*?[^`])\\s*\\1(?!`)/,\n        br: /^ {2,}\\n(?!\\s*$)/,\n        del: noop,\n        text: /^[\\s\\S]+?(?=[\\\\<!\\[_*`]| {2,}\\n|$)/\n    };\n\n    inline._inside = /(?:\\[[^\\]]*\\]|[^\\]]|\\](?=[^\\[]*\\]))*/;\n    inline._href = /\\s*<?(.*?)>?(?:\\s+['\"]([\\s\\S]*?)['\"])?\\s*/;\n\n    inline.link = replace(inline.link)\n        ('inside', inline._inside)\n        ('href', inline._href)\n        ();\n\n    inline.reflink = replace(inline.reflink)\n        ('inside', inline._inside)\n        ();\n\n    /**\n     * Normal Inline Grammar\n     */\n\n    inline.normal = merge({}, inline);\n\n    /**\n     * Pedantic Inline Grammar\n     */\n\n    inline.pedantic = merge({}, inline.normal, {\n        strong: /^__(?=\\S)([\\s\\S]*?\\S)__(?!_)|^\\*\\*(?=\\S)([\\s\\S]*?\\S)\\*\\*(?!\\*)/,\n        em: /^_(?=\\S)([\\s\\S]*?\\S)_(?!_)|^\\*(?=\\S)([\\s\\S]*?\\S)\\*(?!\\*)/\n    });\n\n    /**\n     * GFM Inline Grammar\n     */\n\n    inline.gfm = merge({}, inline.normal, {\n        escape: replace(inline.escape)('])', '~|])')(),\n        url: /^(https?:\\/\\/[^\\s<]+[^<.,:;\"')\\]\\s])/,\n        del: /^~~(?=\\S)([\\s\\S]*?\\S)~~/,\n        text: replace(inline.text)\n            (']|', '~]|')\n            ('|', '|https?://|')\n            ()\n    });\n\n    /**\n     * GFM + Line Breaks Inline Grammar\n     */\n\n    inline.breaks = merge({}, inline.gfm, {\n        br: replace(inline.br)('{2,}', '*')(),\n        text: replace(inline.gfm.text)('{2,}', '*')()\n    });\n\n    /**\n     * Inline Lexer & Compiler\n     */\n\n    function InlineLexer(links, options) {\n        this.options = options || marked.defaults;\n        this.links = links;\n        this.rules = inline.normal;\n\n        if (!this.links) {\n            throw new\n                Error('Tokens array requires a `links` property.');\n        }\n\n        if (this.options.gfm) {\n            if (this.options.breaks) {\n                this.rules = inline.breaks;\n            } else {\n                this.rules = inline.gfm;\n            }\n        } else if (this.options.pedantic) {\n            this.rules = inline.pedantic;\n        }\n    }\n\n    /**\n     * Expose Inline Rules\n     */\n\n    InlineLexer.rules = inline;\n\n    /**\n     * Static Lexing/Compiling Method\n     */\n\n    InlineLexer.output = function (src, links, options) {\n        var inline = new InlineLexer(links, options);\n        return inline.output(src);\n    };\n\n    /**\n     * Lexing/Compiling\n     */\n\n    InlineLexer.prototype.output = function (src) {\n        var out = ''\n            , link\n            , text\n            , href\n            , cap;\n\n        while (src) {\n            // escape\n            if (cap = this.rules.escape.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += cap[1];\n                continue;\n            }\n\n            // autolink\n            if (cap = this.rules.autolink.exec(src)) {\n                src = src.substring(cap[0].length);\n                if (cap[2] === '@') {\n                    text = cap[1][6] === ':'\n                        ? this.mangle(cap[1].substring(7))\n                        : this.mangle(cap[1]);\n                    href = this.mangle('mailto:') + text;\n                } else {\n                    text = escape(cap[1]);\n                    href = text;\n                }\n                out += '<a href=\"'\n                    + href\n                    + '\">'\n                    + text\n                    + '</a>';\n                continue;\n            }\n\n            // url (gfm)\n            if (cap = this.rules.url.exec(src)) {\n                src = src.substring(cap[0].length);\n                text = escape(cap[1]);\n                href = text;\n                out += '<a href=\"'\n                    + href\n                    + '\">'\n                    + text\n                    + '</a>';\n                continue;\n            }\n\n            // tag\n            if (cap = this.rules.tag.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += this.options.sanitize\n                    ? escape(cap[0])\n                    : cap[0];\n                continue;\n            }\n\n            // link\n            if (cap = this.rules.link.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += this.outputLink(cap, {\n                    href: cap[2],\n                    title: cap[3]\n                });\n                continue;\n            }\n\n            // reflink, nolink\n            if ((cap = this.rules.reflink.exec(src))\n                || (cap = this.rules.nolink.exec(src))) {\n                src = src.substring(cap[0].length);\n                link = (cap[2] || cap[1]).replace(/\\s+/g, ' ');\n                link = this.links[link.toLowerCase()];\n                if (!link || !link.href) {\n                    out += cap[0][0];\n                    src = cap[0].substring(1) + src;\n                    continue;\n                }\n                out += this.outputLink(cap, link);\n                continue;\n            }\n\n            // strong\n            if (cap = this.rules.strong.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += '<strong>'\n                    + this.output(cap[2] || cap[1])\n                    + '</strong>';\n                continue;\n            }\n\n            // em\n            if (cap = this.rules.em.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += '<em>'\n                    + this.output(cap[2] || cap[1])\n                    + '</em>';\n                continue;\n            }\n\n            // code\n            if (cap = this.rules.code.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += '<code>'\n                    + escape(cap[2], true)\n                    + '</code>';\n                continue;\n            }\n\n            // br\n            if (cap = this.rules.br.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += '<br>';\n                continue;\n            }\n\n            // del (gfm)\n            if (cap = this.rules.del.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += '<del>'\n                    + this.output(cap[1])\n                    + '</del>';\n                continue;\n            }\n\n            // text\n            if (cap = this.rules.text.exec(src)) {\n                src = src.substring(cap[0].length);\n                out += escape(cap[0]);\n                continue;\n            }\n\n            if (src) {\n                throw new\n                    Error('Infinite loop on byte: ' + src.charCodeAt(0));\n            }\n        }\n\n        return out;\n    };\n\n    /**\n     * Compile Link\n     */\n\n    InlineLexer.prototype.outputLink = function (cap, link) {\n        if (cap[0][0] !== '!') {\n            return '<a href=\"'\n                + escape(link.href)\n                + '\"'\n                + (link.title\n                    ? ' title=\"'\n                    + escape(link.title)\n                    + '\"'\n                    : '')\n                + '>'\n                + this.output(cap[1])\n                + '</a>';\n        } else {\n            return '<img src=\"'\n                + escape(link.href)\n                + '\" alt=\"'\n                + escape(cap[1])\n                + '\"'\n                + (link.title\n                    ? ' title=\"'\n                    + escape(link.title)\n                    + '\"'\n                    : '')\n                + '>';\n        }\n    };\n\n    /**\n     * Smartypants Transformations\n     */\n\n    InlineLexer.prototype.smartypants = function (text) {\n        if (!this.options.smartypants) return text;\n        return text\n            .replace(/--/g, '—')\n            .replace(/'([^']*)'/g, '‘$1’')\n            .replace(/\"([^\"]*)\"/g, '“$1”')\n            .replace(/\\.{3}/g, '…');\n    };\n\n    /**\n     * Mangle Links\n     */\n\n    InlineLexer.prototype.mangle = function (text) {\n        var out = ''\n            , l = text.length\n            , i = 0\n            , ch;\n\n        for (; i < l; i++) {\n            ch = text.charCodeAt(i);\n            if (Math.random() > 0.5) {\n                ch = 'x' + ch.toString(16);\n            }\n            out += '&#' + ch + ';';\n        }\n\n        return out;\n    };\n\n    /**\n     * Parsing & Compiling\n     */\n\n    function Parser(options) {\n        this.tokens = [];\n        this.token = null;\n        this.options = options || marked.defaults;\n    }\n\n    /**\n     * Static Parse Method\n     */\n\n    Parser.parse = function (src, options) {\n        var parser = new Parser(options);\n        return parser.parse(src);\n    };\n\n    /**\n     * Parse Loop\n     */\n\n    Parser.prototype.parse = function (src) {\n        this.inline = new InlineLexer(src.links, this.options);\n        this.tokens = src.reverse();\n\n        var out = '';\n        while (this.next()) {\n            out += this.tok();\n        }\n\n        return out;\n    };\n\n    /**\n     * Next Token\n     */\n\n    Parser.prototype.next = function () {\n        return this.token = this.tokens.pop();\n    };\n\n    /**\n     * Preview Next Token\n     */\n\n    Parser.prototype.peek = function () {\n        return this.tokens[this.tokens.length - 1] || 0;\n    };\n\n    /**\n     * Parse Text Tokens\n     */\n\n    Parser.prototype.parseText = function () {\n        var body = this.token.text;\n\n        while (this.peek().type === 'text') {\n            body += '\\n' + this.next().text;\n        }\n\n        return this.inline.output(body);\n    };\n\n    /**\n     * Parse Current Token\n     */\n\n    Parser.prototype.tok = function () {\n        switch (this.token.type) {\n            case 'space': {\n                return '';\n            }\n            case 'hr': {\n                return '<hr>\\n';\n            }\n            case 'heading': {\n                return '<h'\n                    + this.token.depth\n                    + '>'\n                    + this.inline.output(this.token.text)\n                    + '</h'\n                    + this.token.depth\n                    + '>\\n';\n            }\n            case 'code': {\n                if (this.options.highlight) {\n                    var code = this.options.highlight(this.token.text, this.token.lang);\n                    if (code != null && code !== this.token.text) {\n                        this.token.escaped = true;\n                        this.token.text = code;\n                    }\n                }\n\n                if (!this.token.escaped) {\n                    this.token.text = escape(this.token.text, true);\n                }\n\n                return '<pre><code'\n                    + (this.token.lang\n                        ? ' class=\"'\n                        + this.options.langPrefix\n                        + this.token.lang\n                        + '\"'\n                        : '')\n                    + '>'\n                    + this.token.text\n                    + '</code></pre>\\n';\n            }\n            case 'table': {\n                var body = ''\n                    , heading\n                    , i\n                    , row\n                    , cell\n                    , j;\n\n                // header\n                body += '<thead>\\n<tr>\\n';\n                for (i = 0; i < this.token.header.length; i++) {\n                    heading = this.inline.output(this.token.header[i]);\n                    body += this.token.align[i]\n                        ? '<th align=\"' + this.token.align[i] + '\">' + heading + '</th>\\n'\n                        : '<th>' + heading + '</th>\\n';\n                }\n                body += '</tr>\\n</thead>\\n';\n\n                // body\n                body += '<tbody>\\n'\n                for (i = 0; i < this.token.cells.length; i++) {\n                    row = this.token.cells[i];\n                    body += '<tr>\\n';\n                    for (j = 0; j < row.length; j++) {\n                        cell = this.inline.output(row[j]);\n                        body += this.token.align[j]\n                            ? '<td align=\"' + this.token.align[j] + '\">' + cell + '</td>\\n'\n                            : '<td>' + cell + '</td>\\n';\n                    }\n                    body += '</tr>\\n';\n                }\n                body += '</tbody>\\n';\n\n                return '<table>\\n'\n                    + body\n                    + '</table>\\n';\n            }\n            case 'blockquote_start': {\n                var body = '';\n\n                while (this.next().type !== 'blockquote_end') {\n                    body += this.tok();\n                }\n\n                return '<blockquote>\\n'\n                    + body\n                    + '</blockquote>\\n';\n            }\n            case 'list_start': {\n                var type = this.token.ordered ? 'ol' : 'ul'\n                    , body = '';\n\n                while (this.next().type !== 'list_end') {\n                    body += this.tok();\n                }\n\n                return '<'\n                    + type\n                    + '>\\n'\n                    + body\n                    + '</'\n                    + type\n                    + '>\\n';\n            }\n            case 'list_item_start': {\n                var body = '';\n\n                while (this.next().type !== 'list_item_end') {\n                    body += this.token.type === 'text'\n                        ? this.parseText()\n                        : this.tok();\n                }\n\n                return '<li>'\n                    + body\n                    + '</li>\\n';\n            }\n            case 'loose_item_start': {\n                var body = '';\n\n                while (this.next().type !== 'list_item_end') {\n                    body += this.tok();\n                }\n\n                return '<li>'\n                    + body\n                    + '</li>\\n';\n            }\n            case 'html': {\n                return !this.token.pre && !this.options.pedantic\n                    ? this.inline.output(this.token.text)\n                    : this.token.text;\n            }\n            case 'paragraph': {\n                return '<p>'\n                    + this.inline.output(this.token.text)\n                    + '</p>\\n';\n            }\n            case 'text': {\n                return '<p>'\n                    + this.parseText()\n                    + '</p>\\n';\n            }\n        }\n    };\n\n    /**\n     * Helpers\n     */\n\n    function escape(html, encode) {\n        return html\n            .replace(!encode ? /&(?!#?\\w+;)/g : /&/g, '&amp;')\n            .replace(/</g, '&lt;')\n            .replace(/>/g, '&gt;')\n            .replace(/\"/g, '&quot;')\n            .replace(/'/g, '&#39;');\n    }\n\n    function replace(regex, opt) {\n        regex = regex.source;\n        opt = opt || '';\n        return function self(name, val) {\n            if (!name) return new RegExp(regex, opt);\n            val = val.source || val;\n            val = val.replace(/(^|[^\\[])\\^/g, '$1');\n            regex = regex.replace(name, val);\n            return self;\n        };\n    }\n\n    function noop() { }\n    noop.exec = noop;\n\n    function merge(obj) {\n        var i = 1\n            , target\n            , key;\n\n        for (; i < arguments.length; i++) {\n            target = arguments[i];\n            for (key in target) {\n                if (Object.prototype.hasOwnProperty.call(target, key)) {\n                    obj[key] = target[key];\n                }\n            }\n        }\n\n        return obj;\n    }\n\n    /**\n     * Marked\n     */\n\n    function marked(src, opt, callback) {\n        if (callback || typeof opt === 'function') {\n            if (!callback) {\n                callback = opt;\n                opt = null;\n            }\n\n            if (opt) opt = merge({}, marked.defaults, opt);\n\n            var tokens = Lexer.lex(tokens, opt)\n                , highlight = opt.highlight\n                , pending = 0\n                , l = tokens.length\n                , i = 0;\n\n            if (!highlight || highlight.length < 3) {\n                return callback(null, Parser.parse(tokens, opt));\n            }\n\n            var done = function () {\n                delete opt.highlight;\n                var out = Parser.parse(tokens, opt);\n                opt.highlight = highlight;\n                return callback(null, out);\n            };\n\n            for (; i < l; i++) {\n                (function (token) {\n                    if (token.type !== 'code') return;\n                    pending++;\n                    return highlight(token.text, token.lang, function (err, code) {\n                        if (code == null || code === token.text) {\n                            return --pending || done();\n                        }\n                        token.text = code;\n                        token.escaped = true;\n                        --pending || done();\n                    });\n                })(tokens[i]);\n            }\n\n            return;\n        }\n        try {\n            if (opt) opt = merge({}, marked.defaults, opt);\n            return Parser.parse(Lexer.lex(src, opt), opt);\n        } catch (e) {\n            e.message += '\\nPlease report this to https://github.com/chjj/marked.';\n            if ((opt || marked.defaults).silent) {\n                return '<p>An error occured:</p><pre>'\n                    + escape(e.message + '', true)\n                    + '</pre>';\n            }\n            throw e;\n        }\n    }\n\n    /**\n     * Options\n     */\n\n    marked.options =\n        marked.setOptions = function (opt) {\n            merge(marked.defaults, opt);\n            return marked;\n        };\n\n    marked.defaults = {\n        gfm: true,\n        tables: true,\n        breaks: false,\n        pedantic: false,\n        sanitize: false,\n        smartLists: false,\n        silent: false,\n        highlight: null,\n        langPrefix: 'lang-'\n    };\n\n    /**\n     * Expose\n     */\n\n    marked.Parser = Parser;\n    marked.parser = Parser.parse;\n\n    marked.Lexer = Lexer;\n    marked.lexer = Lexer.lex;\n\n    marked.InlineLexer = InlineLexer;\n    marked.inlineLexer = InlineLexer.output;\n\n    marked.parse = marked;\n\n    if (typeof exports === 'object') {\n        module.exports = marked;\n    } else if (typeof define === 'function' && define.amd) {\n        define(function () { return marked; });\n    } else {\n        this.marked = marked;\n    }\n\n}).call(function () {\n    return this || (typeof window !== 'undefined' ? window : global);\n}());\n\n(function ($) {\n    'use strict';\n\n    // hide the whole page so we dont see the DOM flickering\n    // will be shown upon page load complete or error\n    $('html').addClass('md-hidden-load');\n\n    // register our $.md object\n    $.md = function (method) {\n        if ($.md.publicMethods[method]) {\n            return $.md.publicMethods[method].apply(this,\n                Array.prototype.slice.call(arguments, 1)\n            );\n        } else {\n            $.error('Method ' + method + ' does not exist on jquery.md');\n        }\n    };\n    // default config\n    $.md.config = {\n        title: null,\n        useSideMenu: true,\n        lineBreaks: 'gfm',\n        additionalFooterText: '',\n        anchorCharacter: '&para;',\n        tocAnchor: '[ &uarr; ]'\n    };\n\n    if (!$.mdContentRoot) {\n        $.mdContentRoot = '';\n    }\n\n    $.md.gimmicks = [];\n    $.md.stages = [];\n\n    // the location of the main markdown file we display\n    $.md.mainHref = '';\n\n    // the in-page anchor that is specified after the !\n    $.md.inPageAnchor = '';\n\n\n    $.md.loglevel = {\n        TRACE: 10,\n        DEBUG: 20,\n        INFO: 30,\n        WARN: 40,\n        ERROR: 50,\n        FATAL: 60\n    };\n    // $.md.logThreshold = $.md.loglevel.DEBUG;\n    $.md.logThreshold = $.md.loglevel.WARN;\n\n}(jQuery));\n\n(function ($) {\n    'use strict';\n    $.md.getLogger = function () {\n\n        var loglevel = $.md.loglevel;\n\n        var log = function (logtarget) {\n            var self = this;\n            var level = loglevel[logtarget];\n            return function (msg) {\n                if ($.md.logThreshold <= level) {\n                    console.log('[' + logtarget + '] ' + msg);\n                }\n            };\n        };\n\n        var logger = {};\n        logger.trace = log('TRACE');\n        logger.debug = log('DEBUG');\n        logger.info = log('INFO');\n        logger.warn = log('WARN');\n        logger.error = log('ERROR');\n        logger.fatal = log('FATAL');\n\n        return logger;\n    };\n}(jQuery));\n\n(function ($) {\n    'use strict';\n    var log = $.md.getLogger();\n\n    $.Stage = function (name) {\n        var self = $.extend($.Deferred(), {});\n        self.name = name;\n        self.events = [];\n        self.started = false;\n\n        self.reset = function () {\n            self.complete = $.Deferred();\n            self.outstanding = [];\n        };\n\n        self.reset();\n\n        self.subscribe = function (fn) {\n            if (self.started) {\n                $.error('Subscribing to stage which already started!');\n            }\n            self.events.push(fn);\n        };\n        self.unsubscribe = function (fn) {\n            self.events.remove(fn);\n        };\n\n        self.executeSubscribedFn = function (fn) {\n            var d = $.Deferred();\n            self.outstanding.push(d);\n\n            // display an error if our done() callback is not called\n            $.md.util.wait(2500).done(function () {\n                if (d.state() !== 'resolved') {\n                    log.fatal('Timeout reached for done callback in stage: ' + self.name +\n                        '. Did you forget a done() call in a .subscribe() ?');\n                    log.fatal('stage ' + name + ' failed running subscribed function: ' + fn);\n                }\n            });\n\n            var done = function () {\n                d.resolve();\n            };\n            fn(done);\n        };\n\n        self.run = function () {\n            self.started = true;\n            $(self.events).each(function (i, fn) {\n                self.executeSubscribedFn(fn);\n            });\n\n            // if no events are in our queue, we resolve immediately\n            if (self.outstanding.length === 0) {\n                self.resolve();\n            }\n\n            // we resolve when all our registered events have completed\n            $.when.apply($, self.outstanding)\n                .done(function () {\n                    self.resolve();\n                })\n                .fail(function () {\n                    self.resolve();\n                });\n        };\n\n        self.done(function () {\n            log.debug('stage ' + self.name + ' completed successfully.');\n        });\n        self.fail(function () {\n            log.debug('stage ' + self.name + ' completed with errors!');\n        });\n        return self;\n    };\n}(jQuery));\n\n(function ($) {\n    'use strict';\n\n    var log = $.md.getLogger();\n\n    function init() {\n        $.md.stages = [\n            $.Stage('init'),\n\n            // loads config, initial markdown and navigation\n            $.Stage('load'),\n\n            // will transform the markdown to html\n            $.Stage('transform'),\n\n            // HTML transformation finished\n            $.Stage('ready'),\n\n            // after we have a polished html skeleton\n            $.Stage('skel_ready'),\n\n            // will bootstrapify the skeleton\n            $.Stage('bootstrap'),\n\n            // before we run any gimmicks\n            $.Stage('pregimmick'),\n\n            // after we have bootstrapified the skeleton\n            $.Stage('gimmick'),\n\n            // postprocess\n            $.Stage('postgimmick'),\n\n            $.Stage('all_ready'),\n\n            // used for integration tests, not intended to use in MDwiki itself\n            $.Stage('final_tests')\n        ];\n\n        $.md.stage = function (name) {\n            var m = $.grep($.md.stages, function (e, i) {\n                return e.name === name;\n            });\n            if (m.length === 0) {\n                $.error('A stage by name ' + name + '  does not exist');\n            } else {\n                return m[0];\n            }\n        };\n    }\n    init();\n\n    function resetStages() {\n        var old_stages = $.md.stages;\n        $.md.stages = [];\n        $(old_stages).each(function (i, e) {\n            $.md.stages.push($.Stage(e.name));\n        });\n    }\n\n    var publicMethods = {};\n    $.md.publicMethods = $.extend({}, $.md.publicMethods, publicMethods);\n\n    function transformMarkdown(markdown) {\n        var options = {\n            gfm: true,\n            tables: true,\n            breaks: true\n        };\n        if ($.md.config.lineBreaks === 'original')\n            options.breaks = false;\n        else if ($.md.config.lineBreaks === 'gfm')\n            options.breaks = true;\n\n        marked.setOptions(options);\n\n        // get sample markdown\n        var uglyHtml = marked(markdown);\n        return uglyHtml;\n    }\n\n    function registerFetchMarkdown() {\n\n        var md = '';\n\n        $.md.stage('init').subscribe(function (done) {\n            var ajaxReq = {\n                url: $.mdContentRoot + $.md.mainHref,\n                dataType: 'text'\n            };\n            $.ajax(ajaxReq).done(function (data) {\n                // TODO do this elsewhere\n                md = data;\n                done();\n            }).fail(function () {\n                var log = $.md.getLogger();\n                log.fatal('Could not get ' + $.md.mainHref);\n                done();\n            });\n        });\n\n        // find baseUrl\n        $.md.stage('transform').subscribe(function (done) {\n            var len = $.md.mainHref.lastIndexOf('/');\n            var baseUrl = $.md.mainHref.substring(0, len + 1);\n            $.md.baseUrl = baseUrl;\n            done();\n        });\n\n        $.md.stage('transform').subscribe(function (done) {\n            var uglyHtml = transformMarkdown(md);\n            $('#md-content').html(uglyHtml);\n            md = '';\n            var dfd = $.Deferred();\n            loadExternalIncludes(dfd);\n            dfd.always(function () {\n                done();\n            });\n        });\n    }\n\n    // load [include](/foo/bar.md) external links\n    function loadExternalIncludes(parent_dfd) {\n\n        function findExternalIncludes() {\n            return $('a').filter(function () {\n                var href = $(this).attr('href');\n                var text = $(this).toptext();\n                var isMarkdown = $.md.util.hasMarkdownFileExtension(href);\n                var isInclude = text === 'include';\n                var isPreview = text.startsWith('preview:');\n                return (isInclude || isPreview) && isMarkdown;\n            });\n        }\n\n        function selectPreviewElements($jqcol, num_elements) {\n            function isTextNode(node) {\n                return node.nodeType === 3;\n            }\n            var count = 0;\n            var elements = [];\n            $jqcol.each(function (i, e) {\n                if (count < num_elements) {\n                    elements.push(e);\n                    if (!isTextNode(e)) count++;\n                }\n            });\n            return $(elements);\n        }\n\n        var external_links = findExternalIncludes();\n        // continue execution when all external resources are fully loaded\n        var latch = $.md.util.countDownLatch(external_links.length);\n        latch.always(function () {\n            parent_dfd.resolve();\n        });\n\n        external_links.each(function (i, e) {\n            var $el = $(e);\n            var href = $el.attr('href');\n            var text = $el.toptext();\n\n            $.ajax({\n                url: $.mdContentRoot + href,\n                dataType: 'text'\n            })\n                .done(function (data) {\n                    var $html = $(transformMarkdown(data));\n                    if (text.startsWith('preview:')) {\n                        // only insert the selected number of paragraphs; default 3\n                        var num_preview_elements = parseInt(text.substring(8), 10) || 3;\n                        var $preview = selectPreviewElements($html, num_preview_elements);\n                        $preview.last().append('<a href=\"' + href + '\"> ...read more &#10140;</a>');\n                        $preview.insertBefore($el.parent('p').eq(0));\n                        $el.remove();\n                    } else {\n                        $html.insertAfter($el.parents('p'));\n                        $el.remove();\n                    }\n                }).always(function () {\n                    latch.countDown();\n                });\n        });\n    }\n\n    function isSpecialLink(href) {\n        if (!href) return false;\n\n        if (href.lastIndexOf('data:') >= 0)\n            return true;\n\n        if (href.startsWith('mailto:'))\n            return true;\n\n        if (href.startsWith('file:'))\n            return true;\n\n        if (href.startsWith('ftp:'))\n            return true;\n\n        // TODO capture more special links: every non-http link with : like\n        // torrent:// etc.\n    }\n\n    // modify internal links so we load them through our engine\n    function processPageLinks(domElement, baseUrl) {\n        var html = $(domElement);\n        if (baseUrl === undefined) {\n            baseUrl = '';\n        }\n        // HACK against marked: empty links will have empy href attribute\n        // we remove the href attribute from the a tag\n        html.find('a').not('#md-menu a').filter(function () {\n            var $this = $(this);\n            var attr = $this.attr('href');\n            if (!attr || attr.length === 0)\n                $this.removeAttr('href');\n        });\n\n        html.find('a, img').each(function (i, e) {\n            var link = $(e);\n            // link must be jquery collection\n            var isImage = false;\n            var hrefAttribute = 'href';\n\n            if (!link.attr(hrefAttribute)) {\n                isImage = true;\n                hrefAttribute = 'src';\n            }\n            var href = link.attr(hrefAttribute);\n\n            if (href && href.lastIndexOf('#!') >= 0)\n                return;\n\n            if (isSpecialLink(href))\n                return;\n\n            if (!isImage && href.startsWith('#') && !href.startsWith('#!')) {\n                // in-page link\n                link.click(function (ev) {\n                    ev.preventDefault();\n                    $.md.scrollToInPageAnchor(href);\n                });\n            }\n\n            if (!$.md.util.isRelativeUrl(href))\n                return;\n\n            if (isImage && !$.md.util.isRelativePath(href))\n                return;\n\n            if (!isImage && $.md.util.isGimmickLink(link))\n                return;\n\n            function build_link(url) {\n                if ($.md.util.hasMarkdownFileExtension(url))\n                    return '#!' + url;\n                else\n                    return url;\n            }\n\n            var newHref = baseUrl + href;\n            if (isImage)\n                link.attr(hrefAttribute, newHref);\n            else if ($.md.util.isRelativePath(href))\n                link.attr(hrefAttribute, build_link(newHref));\n            else\n                link.attr(hrefAttribute, build_link(href));\n        });\n    }\n\n    var navMD = '';\n    $.md.NavigationDfd = $.Deferred();\n    var ajaxReq = {\n        url: $.mdContentRoot + 'navigation.md',\n        dataType: 'text'\n    };\n    $.ajax(ajaxReq).done(function (data) {\n        navMD = data;\n        $.md.NavigationDfd.resolve();\n    }).fail(function () {\n        $.md.NavigationDfd.reject();\n    });\n\n    function registerBuildNavigation() {\n\n        $.md.stage('init').subscribe(function (done) {\n            $.md.NavigationDfd.done(function () {\n                done();\n            })\n                .fail(function () {\n                    done();\n                });\n        });\n\n        $.md.stage('transform').subscribe(function (done) {\n            if (navMD === '') {\n                var log = $.md.getLogger();\n                log.info('no navgiation.md found, not using a navbar');\n                done();\n                return;\n            }\n\n            var navHtml = marked(navMD);\n            // TODO why are <script> tags from navHtml APPENDED to the jqcol?\n            var $h = $('<div>' + navHtml + '</div>');\n\n            // insert <scripts> from navigation.md into the DOM\n            $h.each(function (i, e) {\n                if (e.tagName === 'SCRIPT') {\n                    $('script').first().before(e);\n                }\n            });\n\n            // TODO .html() is evil!!!\n            var $navContent = $h.eq(0);\n            $navContent.find('p').each(function (i, e) {\n                var $el = $(e);\n                $el.replaceWith($el.html());\n            });\n            $('#md-menu').append($navContent.html());\n            done();\n        });\n\n        $.md.stage('bootstrap').subscribe(function (done) {\n            processPageLinks($('#md-menu'));\n            done();\n        });\n\n        $.md.stage('postgimmick').subscribe(function (done) {\n            var num_links = $('#md-menu a').length;\n            var has_header = $('#md-menu .navbar-brand').eq(0).toptext().trim().length > 0;\n            if (!has_header && num_links <= 1)\n                $('#md-menu').hide();\n\n            done();\n        });\n    }\n\n    $.md.ConfigDfd = $.Deferred();\n    $.ajax({ url: $.mdContentRoot + 'config.json', dataType: 'text' }).done(function (data) {\n        try {\n            var data_json = JSON.parse(data);\n            $.md.config = $.extend($.md.config, data_json);\n            log.info('Found a valid config.json file, using configuration');\n        } catch (err) {\n            log.error('config.json was not JSON parsable: ' + err);\n        }\n        $.md.ConfigDfd.resolve();\n    }).fail(function (err, textStatus) {\n        log.error('unable to retrieve config.json: ' + textStatus);\n        $.md.ConfigDfd.reject();\n    });\n    function registerFetchConfig() {\n\n        $.md.stage('init').subscribe(function (done) {\n            // TODO 404 won't get cached, requesting it every reload is not good\n            // maybe use cookies? or disable re-loading of the page\n            //$.ajax('config.json').done(function(data){\n            $.md.ConfigDfd.done(function () {\n                done();\n            }).fail(function () {\n                var log = $.md.getLogger();\n                log.info('No config.json found, using default settings');\n                done();\n            });\n        });\n    }\n\n    function registerClearContent() {\n\n        $.md.stage('init').subscribe(function (done) {\n            $('#md-all').empty();\n            var skel = '<div id=\"md-body\"><div id=\"md-title\"></div><div id=\"md-menu\">' +\n                '</div><div id=\"md-content\"></div></div>';\n            $('#md-all').prepend($(skel));\n            done();\n        });\n\n    }\n    function loadContent(href) {\n        $.md.mainHref = href;\n\n        registerFetchMarkdown();\n        registerClearContent();\n\n        // find out which link gimmicks we need\n        $.md.stage('ready').subscribe(function (done) {\n            $.md.initializeGimmicks();\n            $.md.registerLinkGimmicks();\n            done();\n        });\n\n        // wire up the load method of the modules\n        $.each($.md.gimmicks, function (i, module) {\n            if (module.load === undefined) {\n                return;\n            }\n            $.md.stage('load').subscribe(function (done) {\n                module.load();\n                done();\n            });\n        });\n\n        $.md.stage('ready').subscribe(function (done) {\n            $.md('createBasicSkeleton');\n            done();\n        });\n\n        $.md.stage('bootstrap').subscribe(function (done) {\n            $.mdbootstrap('bootstrapify');\n            processPageLinks($('#md-content'), $.md.baseUrl);\n            done();\n        });\n        runStages();\n    }\n\n    function runStages() {\n\n        // wire the stages up\n        $.md.stage('init').done(function () {\n            $.md.stage('load').run();\n        });\n        $.md.stage('load').done(function () {\n            $.md.stage('transform').run();\n        });\n        $.md.stage('transform').done(function () {\n            $.md.stage('ready').run();\n        });\n        $.md.stage('ready').done(function () {\n            $.md.stage('skel_ready').run();\n        });\n        $.md.stage('skel_ready').done(function () {\n            $.md.stage('bootstrap').run();\n        });\n        $.md.stage('bootstrap').done(function () {\n            $.md.stage('pregimmick').run();\n        });\n        $.md.stage('pregimmick').done(function () {\n            $.md.stage('gimmick').run();\n        });\n        $.md.stage('gimmick').done(function () {\n            $.md.stage('postgimmick').run();\n        });\n        $.md.stage('postgimmick').done(function () {\n            $.md.stage('all_ready').run();\n        });\n        $.md.stage('all_ready').done(function () {\n            $('html').removeClass('md-hidden-load');\n\n            // phantomjs hook when we are done\n            if (typeof window.callPhantom === 'function') {\n                window.callPhantom({});\n            }\n\n            $.md.stage('final_tests').run();\n        });\n        $.md.stage('final_tests').done(function () {\n            // reset the stages for next iteration\n            resetStages();\n\n            // required by dalekjs so we can wait the element to appear\n            $('body').append('<span id=\"start-tests\"></span>');\n            $('#start-tests').hide();\n        });\n\n        // trigger the whole process by runing the init stage\n        $.md.stage('init').run();\n        return;\n    }\n\n    function extractHashData() {\n        // first char is the # or #!\n        var href;\n        if (window.location.hash.startsWith('#!')) {\n            href = window.location.hash.substring(2);\n        } else {\n            href = window.location.hash.substring(1);\n        }\n        href = decodeURIComponent(href);\n\n        // extract possible in-page anchor\n        var ex_pos = href.indexOf('#');\n        if (ex_pos !== -1) {\n            $.md.inPageAnchor = href.substring(ex_pos + 1);\n            $.md.mainHref = href.substring(0, ex_pos);\n        } else {\n            $.md.mainHref = href;\n        }\n    }\n\n    function appendDefaultFilenameToHash() {\n        var newHashString = '';\n        var currentHashString = window.location.hash || '';\n        if (currentHashString === '' ||\n            currentHashString === '#' ||\n            currentHashString === '#!') {\n            newHashString = '#!index.md';\n        }\n        else if (currentHashString.startsWith('#!') &&\n            currentHashString.endsWith('/')\n        ) {\n            newHashString = currentHashString + 'index.md';\n        }\n        if (newHashString)\n            window.location.hash = newHashString;\n    }\n\n    $(document).ready(function () {\n\n        // stage init stuff\n        registerFetchConfig();\n        registerBuildNavigation();\n        extractHashData();\n\n        appendDefaultFilenameToHash();\n\n        $(window).bind('hashchange', function () {\n            window.location.reload(false);\n        });\n\n        loadContent($.md.mainHref);\n    });\n}(jQuery));\n\n(function ($) {\n    var publicMethods = {\n        isRelativeUrl: function (url) {\n            if (url === undefined) {\n                return false;\n            }\n            // if there is :// in it, its considered absolute\n            // else its relative\n            if (url.indexOf('://') === -1) {\n                return true;\n            } else {\n                return false;\n            }\n        },\n        isRelativePath: function (path) {\n            if (path === undefined)\n                return false;\n            if (path.startsWith('/'))\n                return false;\n            return true;\n        },\n        isGimmickLink: function (domAnchor) {\n            if (domAnchor.toptext().indexOf('gimmick:') !== -1) {\n                return true;\n            } else {\n                return false;\n            }\n        },\n        hasMarkdownFileExtension: function (str) {\n            var markdownExtensions = ['.md', '.markdown', '.mdown'];\n            var result = false;\n            var value = str.toLowerCase().split('#')[0];\n            $(markdownExtensions).each(function (i, ext) {\n                if (value.toLowerCase().endsWith(ext)) {\n                    result = true;\n                }\n            });\n            return result;\n        },\n        wait: function (time) {\n            return $.Deferred(function (dfd) {\n                setTimeout(dfd.resolve, time);\n            });\n        }\n    };\n    $.md.util = $.extend({}, $.md.util, publicMethods);\n\n    if (typeof String.prototype.startsWith !== 'function') {\n        String.prototype.startsWith = function (str) {\n            return this.slice(0, str.length) === str;\n        };\n    }\n    if (typeof String.prototype.endsWith !== 'function') {\n        String.prototype.endsWith = function (str) {\n            return this.slice(this.length - str.length, this.length) === str;\n        };\n    }\n\n    $.fn.extend({\n        toptext: function () {\n            return this.clone().children().remove().end().text();\n        }\n    });\n\n    // adds a :icontains selector to jQuery that is case insensitive\n    $.expr[':'].icontains = $.expr.createPseudo(function (arg) {\n        return function (elem) {\n            return $(elem).toptext().toUpperCase().indexOf(arg.toUpperCase()) >= 0;\n        };\n    });\n\n    $.md.util.getInpageAnchorText = function (text) {\n        var subhash = text.replace(/ /g, '_');\n        // TODO remove more unwanted characters like ?/,- etc.\n        return subhash;\n\n    };\n    $.md.util.getInpageAnchorHref = function (text, href) {\n        href = href || $.md.mainHref;\n        var subhash = $.md.util.getInpageAnchorText(text);\n        return '#!' + href + '#' + subhash;\n    };\n\n    $.md.util.repeatUntil = function (interval, predicate, maxRepeats) {\n        maxRepeats = maxRepeats || 10;\n        var dfd = $.Deferred();\n        function recursive_repeat(interval, predicate, maxRepeats) {\n            if (maxRepeats === 0) {\n                dfd.reject();\n                return;\n            }\n            if (predicate()) {\n                dfd.resolve();\n                return;\n            } else {\n                $.md.util.wait(interval).always(function () {\n                    recursive_repeat(interval, predicate, maxRepeats - 1);\n                });\n            }\n        }\n        recursive_repeat(interval, predicate, maxRepeats);\n        return dfd;\n    };\n\n    // a count-down latch as in Java7.\n    $.md.util.countDownLatch = function (capacity, min) {\n        min = min || 0;\n        var dfd = $.Deferred();\n        if (capacity <= min) dfd.resolve();\n        dfd.capacity = capacity;\n        dfd.countDown = function () {\n            dfd.capacity--;\n            if (dfd.capacity <= min)\n                dfd.resolve();\n        };\n        return dfd;\n    };\n\n}(jQuery));\n\n(function ($) {\n    'use strict';\n\n    // PUBLIC API\n    $.md.registerGimmick = function (module) {\n        $.md.gimmicks.push(module);\n        return;\n    };\n\n    // registers a script for a gimmick, that is later dynamically loaded\n    // by the core.\n    // src may be an URL or direct javascript sourcecode. When options.callback\n    // is provided, the done() function is passed to the function and needs to\n    // be called.\n    $.md.registerScript = function (module, src, options) {\n        var scriptinfo = new ScriptInfo({\n            module: module,\n            src: src,\n            options: options\n        });\n        registeredScripts.push(scriptinfo);\n    };\n\n    // same as registerScript but for css. Note that we do not provide a\n    // callback when the load finishes\n    $.md.registerCss = function (module, url, options) {\n        var license = options.license,\n            stage = options.stage || 'skel_ready',\n            callback = options.callback;\n\n        checkLicense(license, module);\n        var tag = '<link rel=\"stylesheet\" href=\"' + url + '\" type=\"text/css\"></link>';\n        $.md.stage(stage).subscribe(function (done) {\n            $('head').append(tag);\n            if (callback !== undefined) {\n                callback(done);\n            } else {\n                done();\n            }\n        });\n    };\n\n    // turns hostname/path links into http://hostname/path links\n    // we need to do this because if accessed by file:///, we need a different\n    // transport scheme for external resources (like http://)\n    $.md.prepareLink = function (link, options) {\n        options = options || {};\n        var ownProtocol = window.location.protocol;\n\n        if (options.forceSSL)\n            return 'https://' + link;\n        if (options.forceHTTP)\n            return 'http://' + link;\n\n        if (ownProtocol === 'file:') {\n            return 'http://' + link;\n        }\n        // default: use the same as origin resource\n        return '//' + link;\n    };\n\n    // associate a link trigger for a gimmick. i.e. [gimmick:foo]() then\n    // foo is the trigger and will invoke the corresponding gimmick\n    $.md.linkGimmick = function (module, trigger, callback, stage) {\n        if (stage === undefined) {\n            stage = 'gimmick';\n        }\n        var linktrigger = new LinkTrigger({\n            trigger: trigger,\n            module: module,\n            stage: stage,\n            callback: callback\n        });\n        linkTriggers.push(linktrigger);\n    };\n\n    $.md.triggerIsActive = function (trigger) {\n        if (activeLinkTriggers.indexOf(trigger) === -1) {\n            return false;\n        } else {\n            return true;\n        }\n    };\n\n    var initialized = false;\n    // TODO combine main.js and modules.js closure\n    $.md.initializeGimmicks = function () {\n        findActiveLinkTrigger();\n        runGimmicksOnce();\n        loadRequiredScripts();\n    };\n\n    // END PUBLIC API\n\n\n    var log = $.md.getLogger();\n\n    // triggers that we actually found on the page\n    // array of string\n    var activeLinkTriggers = [];\n\n\n    // array of ScriptInfo\n    var registeredScripts = [];\n    function ScriptInfo(initial) {\n        this.module = undefined;\n        this.options = {};\n\n        // can ba an URL or javascript sourcecode\n        this.src = '';\n\n        $.extend(this, initial);\n    }\n\n    // array of linkTriggers\n    var linkTriggers = [];\n    function LinkTrigger(initial) {\n        this.trigger = undefined;\n        this.module = undefined;\n        this.callback = undefined;\n\n        $.extend(this, initial);\n    }\n\n    // jQuery does some magic when inserting inline scripts, so better\n    // use vanilla JS. See:\n    // http://stackoverflow.com/questions/610995/jquery-cant-append-script-element\n    function insertInlineScript(src) {\n        // scripts always need to go directly into the DOM\n        var script = document.createElement('script');\n        script.type = 'text/javascript';\n        script.text = src;\n        document.body.appendChild(script);\n    }\n\n    // since we are GPL, we have to be cautious what other scripts we load\n    // as delivering to the browser is considered delivering a derived work\n    var licenses = ['MIT', 'BSD', 'GPL', 'GPL2', 'GPL3', 'LGPL', 'LGPL2',\n        'APACHE2', 'PUBLICDOMAIN', 'EXCEPTION', 'OTHER'\n    ];\n    function checkLicense(license, module) {\n        if ($.inArray(license, licenses) === -1) {\n            var availLicenses = JSON.stringify(licenses);\n            log.warn('license ' + license + ' is not known.');\n            log.warn('Known licenses:' + availLicenses);\n\n        } else if (license === 'OTHER') {\n            log.warn('WARNING: Module ' + module.name + ' uses a script' +\n                ' with unknown license. This may be a GPL license violation if' +\n                ' this website is publically available!');\n        }\n    }\n\n    // will actually schedule the script load into the DOM.\n    function loadScript(scriptinfo) {\n\n        var module = scriptinfo.module,\n            src = scriptinfo.src,\n            options = scriptinfo.options;\n\n        var license = options.license || 'OTHER',\n            loadstage = options.loadstage || 'skel_ready',\n            finishstage = options.finishstage || 'pregimmick',\n            callback = options.callback;\n\n        var loadDone = $.Deferred();\n\n        checkLicense(license, module);\n        // start script loading\n        log.debug('subscribing ' + module.name + ' to start: ' + loadstage + ' end in: ' + finishstage);\n        $.md.stage(loadstage).subscribe(function (done) {\n            if (src.startsWith('//') || src.startsWith('http')) {\n                $.getScript(src, function () {\n                    if (callback !== undefined) {\n                        callback(done);\n                    } else {\n                        log.debug('module' + module.name + ' script load done: ' + src);\n                        done();\n                    }\n                    loadDone.resolve();\n                });\n            } else {\n                // inline script that we directly insert\n                insertInlineScript(src);\n                log.debug('module' + module.name + ' script inject done');\n                loadDone.resolve();\n                done();\n            }\n        });\n        // if loading is not yet finished in stage finishstage, wait\n        // for the loading to complete\n        $.md.stage(finishstage).subscribe(function (done) {\n            loadDone.done(function () {\n                done();\n            });\n        });\n    }\n\n    // finds out that kind of trigger words are acutally used on a given page\n    // this is most likely a very small subset of all available gimmicks\n    function findActiveLinkTrigger() {\n        var $gimmicks = $('a:icontains(gimmick:)');\n        $gimmicks.each(function (i, e) {\n            var parts = getGimmickLinkParts($(e));\n            if (activeLinkTriggers.indexOf(parts.trigger) === -1) {\n                activeLinkTriggers.push(parts.trigger);\n            }\n        });\n        log.debug('Scanning for required gimmick links: ' + JSON.stringify(activeLinkTriggers));\n    }\n\n    function loadRequiredScripts() {\n        // find each module responsible for the link trigger\n        $.each(activeLinkTriggers, function (i, trigger) {\n            var module = findModuleByTrigger(trigger);\n            if (module === undefined) {\n                log.error('Gimmick link: \"' + trigger + '\" found but no suitable gimmick loaded');\n                return;\n            }\n            var scriptinfo = registeredScripts.filter(function (info) {\n                return info.module.name === module.name;\n            })[0];\n            // register to load the script\n            if (scriptinfo !== undefined) {\n                loadScript(scriptinfo);\n            }\n        });\n    }\n\n    function findModuleByTrigger(trigger) {\n        var ret;\n        $.each(linkTriggers, function (i, e) {\n            if (e.trigger === trigger) {\n                ret = e.module;\n            }\n        });\n        return ret;\n    }\n\n    function getGimmickLinkParts($link) {\n        var link_text = $.trim($link.toptext());\n        // returns linkTrigger, options, linkText\n        if (link_text.match(/gimmick:/i) === null) {\n            return null;\n        }\n        var href = $.trim($link.attr('href'));\n        var r = new RegExp(/gimmick:\\s*([^(\\s]*)\\s*(\\(\\s*{?(.*)\\s*}?\\s*\\))*/i);\n        var matches = r.exec(link_text);\n        if (matches === null || matches[1] === undefined) {\n            $.error('Error matching a gimmick: ' + link_text);\n            return null;\n        }\n        var trigger = matches[1].toLowerCase();\n        var args = null;\n        // getting the parameters\n        if (matches[2] !== undefined) {\n            // remove whitespaces\n            var params = $.trim(matches[3].toString());\n            // remove the closing } if present\n            if (params.charAt(params.length - 1) === '}') {\n                params = params.substring(0, params.length - 1);\n            }\n            // add surrounding braces and paranthese\n            params = '({' + params + '})';\n            // replace any single quotes by double quotes\n            params = params.replace(/'/g, '\"');\n            // finally, try if the json object is valid\n            try {\n                /*jshint -W061 */\n                args = eval(params);\n            } catch (err) {\n                log.error('error parsing argument of gimmick: ' + link_text + 'giving error: ' + err);\n            }\n        }\n        return { trigger: trigger, options: args, href: href };\n    }\n\n    function runGimmicksOnce() {\n        // runs the once: callback for each gimmick within the init stage\n        $.each($.md.gimmicks, function (i, module) {\n            if (module.once === undefined) {\n                return;\n            }\n            module.once();\n        });\n    }\n\n    // activate all gimmicks on a page, that are contain the text gimmick:\n    // TODO make private / merge closures\n    $.md.registerLinkGimmicks = function () {\n        var $gimmick_links = $('a:icontains(gimmick:)');\n        $gimmick_links.each(function (i, e) {\n            var $link = $(e);\n            var gimmick_arguments = getGimmickLinkParts($link);\n\n            $.each(linkTriggers, function (i, linktrigger) {\n                if (gimmick_arguments.trigger === linktrigger.trigger) {\n                    subscribeLinkTrigger($link, gimmick_arguments, linktrigger);\n                }\n            });\n        });\n    };\n\n    function subscribeLinkTrigger($link, args, linktrigger) {\n        log.debug('Subscribing gimmick ' + linktrigger.module.name + ' to stage: ' + linktrigger.stage);\n\n        $.md.stage(linktrigger.stage).subscribe(function (done) {\n            args.options = args.options || {};\n\n            // it is possible that broken modules or any other transformation removed the $link\n            // from the dom in the meantime\n            if (!jQuery.contains(document.documentElement, $link[0])) {\n                log.error('LINK IS NOT IN THE DOM ANYMORE: ');\n                console.log($link);\n            }\n\n            log.debug('Running gimmick ' + linktrigger.module.name);\n            linktrigger.callback($link, args.options, args.href, done);\n\n            // if the gimmick didn't call done, we trigger it here\n            done();\n        });\n    }\n}(jQuery));\n\n(function ($) {\n    var publicMethods = {\n        createBasicSkeleton: function () {\n\n            setPageTitle();\n            wrapParagraphText();\n            linkImagesToSelf();\n            groupImages();\n            removeBreaks();\n            addInpageAnchors();\n\n            $.md.stage('all_ready').subscribe(function (done) {\n                if ($.md.inPageAnchor !== '') {\n                    $.md.util.wait(500).then(function () {\n                        $.md.scrollToInPageAnchor($.md.inPageAnchor);\n                    });\n                }\n                done();\n            });\n            return;\n\n        }\n    };\n    $.md.publicMethods = $.extend({}, $.md.publicMethods, publicMethods);\n\n    // set the page title to the browser document title, optionally picking\n    // the first h1 element as title if no title is given\n    function setPageTitle() {\n        var $pageTitle;\n        if ($.md.config.title)\n            $('title').text($.md.config.title);\n\n        $pageTitle = $('#md-content h1').eq(0);\n        if ($.trim($pageTitle.toptext()).length > 0) {\n            $('#md-title').prepend($pageTitle);\n            var title = $pageTitle.toptext();\n            // document.title = title;\n        } else {\n            $('#md-title').remove();\n        }\n    }\n    function wrapParagraphText() {\n        // TODO is this true for marked.js?\n\n        // markdown gives us sometime paragraph that contain child tags (like img),\n        // but the containing text is not wrapped. Make sure to wrap the text in the\n        // paragraph into a <div>\n\n        // this also moves ANY child tags to the front of the paragraph!\n        $('#md-content p').each(function () {\n            var $p = $(this);\n            // nothing to do for paragraphs without text\n            if ($.trim($p.text()).length === 0) {\n                // make sure no whitespace are in the p and then exit\n                //$p.text ('');\n                return;\n            }\n            // children elements of the p\n            var children = $p.contents().filter(function () {\n                var $child = $(this);\n                // we extract images and hyperlinks with images out of the paragraph\n                if (this.tagName === 'A' && $child.find('img').length > 0) {\n                    return true;\n                }\n                if (this.tagName === 'IMG') {\n                    return true;\n                }\n                // else\n                return false;\n            });\n            var floatClass = getFloatClass($p);\n            $p.wrapInner('<div class=\"md-text\" />');\n\n            // if there are no children, we are done\n            if (children.length === 0) {\n                return;\n            }\n            // move the children out of the wrapped div into the original p\n            children.prependTo($p);\n\n            // at this point, we now have a paragraph that holds text AND images\n            // we mark that paragraph to be a floating environment\n            // TODO determine floatenv left/right\n            $p.addClass('md-floatenv').addClass(floatClass);\n        });\n    }\n    function removeBreaks() {\n        // since we use non-markdown-standard line wrapping, we get lots of\n        // <br> elements we don't want.\n\n        // remove a leading <br> from floatclasses, that happen to\n        // get insertet after an image\n        $('.md-floatenv').find('.md-text').each(function () {\n            var $first = $(this).find('*').eq(0);\n            if ($first.is('br')) {\n                $first.remove();\n            }\n        });\n\n        // remove any breaks from image groups\n        $('.md-image-group').find('br').remove();\n    }\n    function getFloatClass(par) {\n        var $p = $(par);\n        var floatClass = '';\n\n        // reduce content of the paragraph to images\n        var nonTextContents = $p.contents().filter(function () {\n            if (this.tagName === 'IMG' || this.tagName === 'IFRAME') {\n                return true;\n            }\n            else if (this.tagName === 'A') {\n                return $(this).find('img').length > 0;\n            }\n            else {\n                return $.trim($(this).text()).length > 0;\n            }\n        });\n        // check the first element - if its an image or a link with image, we go left\n        var elem = nonTextContents[0];\n        if (elem !== undefined && elem !== null) {\n            if (elem.tagName === 'IMG' || elem.tagName === 'IFRAME') {\n                floatClass = 'md-float-left';\n            }\n            else if (elem.tagName === 'A' && $(elem).find('img').length > 0) {\n                floatClass = 'md-float-left';\n            }\n            else {\n                floatClass = 'md-float-right';\n            }\n        }\n        return floatClass;\n    }\n    // images are put in the same image group as long as there is\n    // not separating paragraph between them\n    function groupImages() {\n        var par = $('p img').parents('p');\n        // add an .md-image-group class to the p\n        par.addClass('md-image-group');\n    }\n\n    // takes a standard <img> tag and adds a hyperlink to the image source\n    // needed since we scale down images via css and want them to be accessible\n    // in original format\n    function linkImagesToSelf() {\n        function selectNonLinkedImages() {\n            // only select images that do not have a non-empty parent link\n            $images = $('img').filter(function (index) {\n                var $parent_link = $(this).parents('a').eq(0);\n                if ($parent_link.length === 0) return true;\n                var attr = $parent_link.attr('href');\n                return (attr && attr.length === 0);\n            });\n            return $images;\n        }\n        var $images = selectNonLinkedImages();\n        return $images.each(function () {\n            var $this = $(this);\n            var img_src = $this.attr('src');\n            var img_title = $this.attr('title');\n            if (img_title === undefined) {\n                img_title = '';\n            }\n            // wrap the <img> tag in an anchor and copy the title of the image\n            $this.wrap('<a class=\"md-image-selfref\" href=\"' + img_src + '\" title=\"' + img_title + '\"/> ');\n        });\n    }\n\n    function addInpageAnchors() {\n        // adds a pilcrow (paragraph) character to heading with a link for the\n        // inpage anchor\n        function addPilcrow($heading, href) {\n            var c = $.md.config.anchorCharacter;\n            var $pilcrow = $('<span class=\"anchor-highlight\"><a>' + c + '</a></span>');\n            $pilcrow.find('a').attr('href', href);\n            $pilcrow.hide();\n\n            var mouse_entered = false;\n            $heading.mouseenter(function () {\n                mouse_entered = true;\n                $.md.util.wait(300).then(function () {\n                    if (!mouse_entered) return;\n                    $pilcrow.fadeIn(200);\n                });\n            });\n            $heading.mouseleave(function () {\n                mouse_entered = false;\n                $pilcrow.fadeOut(200);\n            });\n            $pilcrow.appendTo($heading);\n        }\n\n        // adds a link to the navigation at the top of the page\n        function addJumpLinkToTOC($heading) {\n            if ($.md.config.useSideMenu === false) return;\n            if ($heading.prop(\"tagName\") !== 'H2') return;\n\n            var c = $.md.config.tocAnchor;\n            if (c === '')\n                return;\n\n            var $jumpLink = $('<a class=\"visible-xs visible-sm jumplink\" href=\"#md-page-menu\">' + c + '</a>');\n            $jumpLink.click(function (ev) {\n                ev.preventDefault();\n\n                $('body').scrollTop($('#md-page-menu').position().top);\n            });\n\n            if ($heading.parents('#md-menu').length === 0) {\n                $jumpLink.insertAfter($heading);\n            }\n        }\n\n        // adds a page inline anchor to each h1,h2,h3,h4,h5,h6 element\n        // which can be accessed by the headings text\n        $('h1,h2,h3,h4,h5,h6').not('#md-title h1').each(function () {\n            var $heading = $(this);\n            $heading.addClass('md-inpage-anchor');\n            var text = $heading.clone().children('.anchor-highlight').remove().end().text();\n            var href = $.md.util.getInpageAnchorHref(text);\n            addPilcrow($heading, href);\n\n            //add jumplink to table of contents\n            //addJumpLinkToTOC($heading);\n        });\n    }\n\n    $.md.scrollToInPageAnchor = function (anchortext) {\n        if (anchortext.startsWith('#'))\n            anchortext = anchortext.substring(1, anchortext.length);\n        // we match case insensitive\n        var doBreak = false;\n        $('.md-inpage-anchor').each(function () {\n            if (doBreak) { return; }\n            var $this = $(this);\n            // don't use the text of any subnode\n            var text = $this.toptext();\n            var match = $.md.util.getInpageAnchorText(text);\n            if (anchortext === match) {\n                this.scrollIntoView(true);\n                var navbar_offset = $('.navbar-collapse').height() + 15;\n                window.scrollBy(0, -navbar_offset + 5);\n                doBreak = true;\n            }\n        });\n    };\n\n}(jQuery));\n\n(function ($) {\n    'use strict';\n    // call the gimmick\n    $.mdbootstrap = function (method) {\n        if ($.mdbootstrap.publicMethods[method]) {\n            return $.mdbootstrap.publicMethods[method].apply(this, Array.prototype.slice.call(arguments, 1));\n        } else {\n            $.error('Method ' + method + ' does not exist on jquery.mdbootstrap');\n        }\n    };\n    // simple wrapper around $().bind\n    $.mdbootstrap.events = [];\n    $.mdbootstrap.bind = function (ev, func) {\n        $(document).bind(ev, func);\n        $.mdbootstrap.events.push(ev);\n    };\n    $.mdbootstrap.trigger = function (ev) {\n        $(document).trigger(ev);\n    };\n\n    var navStyle = '';\n\n    // PUBLIC API functions that are exposed\n    var publicMethods = {\n        bootstrapify: function () {\n            createPageSkeleton();\n            buildMenu();\n            changeHeading();\n            replaceImageParagraphs();\n\n            $('table').addClass('table').addClass('table-bordered');\n            //pullRightBumper ();\n\n            // remove the margin for headings h1 and h2 that are the first\n            // on page\n            //if (navStyle == \"sub\" || (navStyle == \"top\" && $('#md-title').text ().trim ().length === 0))\n            //    $(\".md-first-heading\").css (\"margin-top\", \"0\");\n\n            // external content should run after gimmicks were run\n            $.md.stage('pregimmick').subscribe(function (done) {\n                if ($.md.config.useSideMenu !== false) {\n                    createPageContentMenu();\n                }\n                addFooter();\n                addAdditionalFooterText();\n                done();\n            });\n            $.md.stage('postgimmick').subscribe(function (done) {\n                adjustExternalContent();\n                highlightActiveLink();\n\n                done();\n            });\n        }\n    };\n    // register the public API functions\n    $.mdbootstrap.publicMethods = $.extend({}, $.mdbootstrap.publicMethods, publicMethods);\n\n    // PRIVATE FUNCTIONS:\n\n    function buildTopNav() {\n        // replace with the navbar skeleton\n        if ($('#md-menu').length <= 0) {\n            return;\n        }\n        navStyle = 'top';\n        var $menuContent = $('#md-menu').children();\n\n        // $('#md-menu').addClass ('navbar navbar-default navbar-fixed-top');\n        // var menusrc = '';\n        // menusrc += '<div id=\"md-menu-inner\" class=\"container\">';\n        // menusrc += '<ul id=\"md-menu-ul\" class=\"nav navbar-nav\">';\n        // menusrc += '</ul></div>';\n\n        var navbar = '';\n        navbar += '<div id=\"md-main-navbar\" class=\"navbar navbar-default navbar-fixed-top\" role=\"navigation\">';\n        navbar += '<div class=\"navbar-header\">';\n        navbar += '<button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\".navbar-ex1-collapse\">';\n        navbar += '<span class=\"sr-only\">Toggle navigation</span>';\n        navbar += '<span class=\"icon-bar\"></span>';\n        navbar += '<span class=\"icon-bar\"></span>';\n        navbar += '<span class=\"icon-bar\"></span>';\n        navbar += '</button>';\n        navbar += '<a class=\"navbar-brand\" href=\"#\"></a>';\n        navbar += '</div>';\n\n        navbar += '<div class=\"collapse navbar-collapse navbar-ex1-collapse\">';\n        navbar += '<ul class=\"nav navbar-nav\" />';\n        navbar += '<ul class=\"nav navbar-nav navbar-right\" />';\n        navbar += '</div>';\n        navbar += '</div>';\n        var $navbar = $(navbar);\n\n        $navbar.appendTo('#md-menu');\n        // .eq(0) becase we dont want navbar-right to be appended to\n        $('#md-menu ul.nav').eq(0).append($menuContent);\n\n        // the menu should be the first element in the body\n        $('#md-menu').prependTo('#md-all');\n\n        var brand_text = $('#md-menu h1').toptext();\n        $('#md-menu h1').remove();\n        $('a.navbar-brand').text(brand_text);\n\n        // initial offset\n        $('#md-body').css('margin-top', '70px');\n        $.md.stage('pregimmick').subscribe(function (done) {\n            check_offset_to_navbar();\n            done();\n        });\n    }\n    // the navbar has different height depending on theme, number of navbar entries,\n    // and window/device width. Therefore recalculate on start and upon window resize\n    function set_offset_to_navbar() {\n        var height = $('#md-main-navbar').height() + 10;\n        $('#md-body').css('margin-top', height + 'px');\n    }\n    function check_offset_to_navbar() {\n        // HACK this is VERY UGLY. When an external theme is used, we don't know when the\n        // css style will be finished loading - and we can only correctly calculate\n        // the height AFTER it has completely loaded.\n        var navbar_height = 0;\n\n        var dfd1 = $.md.util.repeatUntil(40, function () {\n            navbar_height = $('#md-main-navbar').height();\n            return (navbar_height > 35) && (navbar_height < 481);\n        }, 25);\n\n        dfd1.done(function () {\n            navbar_height = $('#md-main-navbar').height();\n            set_offset_to_navbar();\n            // now bootstrap changes this maybe after a while, again watch for changes\n            var dfd2 = $.md.util.repeatUntil(20, function () {\n                return navbar_height !== $('#md-main-navbar').height();\n            }, 25);\n            dfd2.done(function () {\n                // it changed, so we need to change it again\n                set_offset_to_navbar();\n            });\n            // and finally, for real slow computers, make sure it is changed if changin very late\n            $.md.util.wait(2000).done(function () {\n                set_offset_to_navbar();\n            });\n        });\n    }\n    function buildSubNav() {\n        // replace with the navbar skeleton\n        /* BROKEN CODE\n        if ($('#md-menu').length <= 0) {\n            return;\n        }\n        navStyle = 'sub';\n        var $menuContent = $('#md-menu').html ();\n\n        var menusrc = '';\n        menusrc += '<div id=\"md-menu-inner\" class=\"subnav\">';\n        menusrc += '<ul id=\"md-menu-ul\" class=\"nav nav-pills\">';\n        menusrc += $menuContent;\n        menusrc += '</ul></div>';\n        $('#md-menu').empty();\n        $('#md-menu').wrapInner($(menusrc));\n        $('#md-menu').addClass ('col-md-12');\n\n        $('#md-menu-container').insertAfter ($('#md-title-container'));\n        */\n    }\n\n    function buildMenu() {\n        if ($('#md-menu a').length === 0) {\n            return;\n        }\n        var h = $('#md-menu');\n\n        // make toplevel <a> a dropdown\n        h.find('> a[href=\"\"]')\n            .attr('data-toggle', 'dropdown')\n            .addClass('dropdown-toggle')\n            .attr('href', '')\n            .append('<b class=\"caret\"/>');\n        h.find('ul').addClass('dropdown-menu');\n        h.find('ul li').addClass('dropdown');\n\n        // replace hr with dividers\n        $('#md-menu hr').each(function (i, e) {\n            var hr = $(e);\n            var prev = hr.prev();\n            var next = hr.next();\n            if (prev.is('ul') && prev.length >= 0) {\n                prev.append($('<li class=\"divider\"/>'));\n                hr.remove();\n                if (next.is('ul')) {\n                    next.find('li').appendTo(prev);\n                    next.remove();\n                }\n                // next ul should now be empty\n            }\n            return;\n        });\n\n        // remove empty uls\n        $('#md-menu ul').each(function (i, e) {\n            var ul = $(e);\n            if (ul.find('li').length === 0) {\n                ul.remove();\n            }\n        });\n\n        $('#md-menu hr').replaceWith($('<li class=\"divider-vertical\"/>'));\n\n\n        // wrap the toplevel links in <li>\n        $('#md-menu > a').wrap('<li />');\n        $('#md-menu ul').each(function (i, e) {\n            var ul = $(e);\n            ul.appendTo(ul.prev());\n            ul.parent('li').addClass('dropdown');\n        });\n\n        // submenu headers\n        $('#md-menu li.dropdown').find('h1, h2, h3').each(function (i, e) {\n            var $e = $(e);\n            var text = $e.toptext();\n            var header = $('<li class=\"dropdown-header\" />');\n            header.text(text);\n            $e.replaceWith(header);\n        });\n\n        // call the user specifed menu function\n        buildTopNav();\n    }\n    function isVisibleInViewport(e) {\n        var el = $(e);\n        var top = $(window).scrollTop();\n        var bottom = top + $(window).height();\n\n        var eltop = el.offset().top;\n        var elbottom = eltop + el.height();\n\n        return (elbottom <= bottom) && (eltop >= top);\n    }\n\n    function createPageContentMenu() {\n\n        // assemble the menu\n        var $headings = $('#md-content').find('h2').clone();\n        // we dont want the text of any child nodes\n        $headings.children().remove();\n\n        if ($headings.length <= 1) {\n            return;\n        }\n\n        $('#md-content').removeClass('col-md-12');\n        $('#md-content').addClass('col-md-9');\n        $('#md-content-row').prepend('<div class=\"col-md-3\" id=\"md-left-column\"/>');\n\n        $(window).scroll(function () {\n            var $first;\n            $('*.md-inpage-anchor').each(function (i, e) {\n                if ($first === undefined) {\n                    var h = $(e);\n                    if (isVisibleInViewport(h)) {\n                        $first = h;\n                    }\n                }\n            });\n            // highlight in the right menu\n            $('#md-page-menu a').each(function (i, e) {\n                var $a = $(e);\n                if ($first && $a.toptext() === $first.toptext()) {\n                    $('#md-page-menu a.active').removeClass('active');\n                    //$a.parent('a').addClass('active');\n                    $a.addClass('active');\n                }\n            });\n        });\n\n\n        var affixDiv = $('<div id=\"md-page-menu\" />');\n\n        var top_spacing = 70;\n        affixDiv.css('top', top_spacing);\n        affixDiv.css('position', 'sticky');\n\n        var $pannel = $('<div class=\"panel panel-default\"><ul class=\"list-group\"/></div>');\n        var $ul = $pannel.find(\"ul\");\n        affixDiv.append($pannel);\n\n        $headings.each(function (i, e) {\n            var $heading = $(e);\n            var $li = $('<li class=\"list-group-item\" />');\n            var $a = $('<a />');\n            $a.attr('href', $.md.util.getInpageAnchorHref($heading.toptext()));\n            $a.click(function (ev) {\n                ev.preventDefault();\n\n                var $this = $(this);\n                var anchortext = $.md.util.getInpageAnchorText($this.toptext());\n                $.md.scrollToInPageAnchor(anchortext);\n            });\n            $a.text($heading.toptext());\n            $li.append($a);\n            $ul.append($li);\n        });\n\n        $(window).resize(function () {\n            check_offset_to_navbar();\n        });\n        $.md.stage('postgimmick').subscribe(function (done) {\n            done();\n        });\n\n        //menu.css('width','100%');\n        $('#md-left-column').append(affixDiv);\n\n    }\n\n    function createPageSkeleton() {\n\n        $('#md-title').wrap('<div class=\"container\" id=\"md-title-container\"/>');\n        $('#md-title').wrap('<div class=\"row\" id=\"md-title-row\"/>');\n\n        $('#md-menu').wrap('<div class=\"container\" id=\"md-menu-container\"/>');\n        $('#md-menu').wrap('<div class=\"row\" id=\"md-menu-row\"/>');\n\n        $('#md-content').wrap('<div class=\"container\" id=\"md-content-container\"/>');\n        $('#md-content').wrap('<div class=\"row\" id=\"md-content-row\"/>');\n\n        $('#md-body').wrap('<div class=\"container\" id=\"md-body-container\"/>');\n        $('#md-body').wrap('<div class=\"row\" id=\"md-body-row\"/>');\n\n        $('#md-title').addClass('col-md-12');\n        $('#md-content').addClass('col-md-12');\n\n    }\n    function pullRightBumper() {\n        /*     $(\"span.bumper\").each (function () {\n                   $this = $(this);\n                   $this.prev().addClass (\"pull-right\");\n               });\n               $('span.bumper').addClass ('pull-right');\n       */\n    }\n\n    function changeHeading() {\n\n        // HEADING\n        var jumbo = $('<div class=\"page-header\" />');\n        $('#md-title').wrapInner(jumbo);\n    }\n\n    function highlightActiveLink() {\n        // when no menu is used, return\n        if ($('#md-menu').find('li').length === 0) {\n            return;\n        }\n        var filename = window.location.hash;\n\n        if (filename.length === 0) {\n            filename = '#!index.md';\n        }\n        var selector = 'li:has(a[href=\"' + filename + '\"])';\n        $('#md-menu').find(selector).addClass('active');\n    }\n\n    // replace all <p> around images with a <div class=\"thumbnail\" >\n    function replaceImageParagraphs() {\n\n        // only select those paragraphs that have images in them\n        var $pars = $('p img').parents('p');\n        $pars.each(function () {\n            var $p = $(this);\n            var $images = $(this).find('img')\n                .filter(function () {\n                    // only select those images that have no parent anchor\n                    return $(this).parents('a').length === 0;\n                })\n                // add those anchors including images\n                .add($(this).find('img'))\n                .addClass('img-responsive')\n                .addClass('img-thumbnail');\n\n            // create a new url group at the fron of the paragraph\n            //$p.prepend($('<ul class=\"thumbnails\" />'));\n            // move the images to the newly created ul\n            //$p.find('ul').eq(0).append($images);\n\n            // wrap each image with a <li> that limits their space\n            // the number of images in a paragraphs determines thei width / span\n\n            // if the image is a link, wrap around the link to avoid\n            function wrapImage($imgages, wrapElement) {\n                return $images.each(function (i, img) {\n                    var $img = $(img);\n                    var $parent_img = $img.parent('a');\n                    if ($parent_img.length > 0)\n                        $parent_img.wrap(wrapElement);\n                    else\n                        $img.wrap(wrapElement);\n                });\n            }\n\n            if ($p.hasClass('md-floatenv')) {\n                if ($images.length === 1) {\n                    wrapImage($images, '<div class=\"col-sm-8\" />');\n                } else if ($images.length === 2) {\n                    wrapImage($images, '<div class=\"col-sm-4\" />');\n                } else {\n                    wrapImage($images, '<div class=\"col-sm-2\" />');\n                }\n            } else {\n\n                // non-float => images are on their own single paragraph, make em larger\n                // but remember, our image resizing will make them only as large as they are\n                // but do no upscaling\n                // TODO replace by calculation\n\n                if ($images.length === 1) {\n                    wrapImage($images, '<div class=\"col-sm-12\" />');\n                } else if ($images.length === 2) {\n                    wrapImage($images, '<div class=\"col-sm-6\" />');\n                } else if ($images.length === 3) {\n                    wrapImage($images, '<div class=\"col-sm-4\" />');\n                } else if ($images.length === 4) {\n                    wrapImage($images, '<div class=\"col-sm-3\" />');\n                } else {\n                    wrapImage($images, '<div class=\"col-sm-2\" />');\n                }\n            }\n            $p.addClass('row');\n            // finally, every img gets its own wrapping thumbnail div\n            //$images.wrap('<div class=\"thumbnail\" />');\n        });\n\n        // apply float to the ul thumbnails\n        //$('.md-floatenv.md-float-left ul').addClass ('pull-left');\n        //$('.md-floatenv.md-float-right ul').addClass ('pull-right');\n    }\n\n    function adjustExternalContent() {\n        // external content are usually iframes or divs that are integrated\n        // by gimmicks\n        // example: youtube iframes, google maps div canvas\n        // all external content are in the md-external class\n\n        $('iframe.md-external').not('.md-external-nowidth')\n            .attr('width', '450')\n            .css('width', '450px');\n\n        $('iframe.md-external').not('.md-external-noheight')\n            .attr('height', '280')\n            .css('height', '280px');\n\n        // make it appear like an image thumbnal\n        //$('.md-external').addClass('img-thumbnail');\n\n        //.wrap($(\"<ul class='thumbnails' />\")).wrap($(\"<li class='col-md-6' />\"));\n        $('div.md-external').not('.md-external-noheight')\n            .css('height', '280px');\n        $('div.md-external').not('.md-external-nowidth')\n            .css('width', '450px');\n\n        // // make it appear like an image thumbnal\n        // $(\"div.md-external\").addClass(\"thumbnail\").wrap($(\"<ul class='thumbnails' />\")).wrap($(\"<li class='col-md-10' />\"));\n\n        // $(\"div.md-external-large\").css('width', \"700px\")\n    }\n\n    // note: the footer is part of the GPLv3 legal information\n    // and may not be removed or hidden to comply with licensing conditions.\n    function addFooter() {\n        var navbar = '';\n        navbar += '<hr><div class=\"scontainer\">';\n        navbar += '<div class=\"pull-right md-copyright-footer\"> ';\n        navbar += '<span id=\"md-footer-additional\"></span>';\n        navbar += 'Website generated with <a href=\"http://www.mdwiki.info\">MDwiki</a> ';\n        navbar += '&copy; Timo D&ouml;rr and contributors. ';\n        navbar += '</div>';\n        navbar += '</div>';\n        var $navbar = $(navbar);\n        $navbar.css('position', 'relative');\n        $navbar.css('margin-top', '1em');\n        $('#md-all').append($navbar);\n    }\n\n    function addAdditionalFooterText() {\n        var text = $.md.config.additionalFooterText;\n        if (text) {\n            $('.md-copyright-footer #md-footer-additional').html(text);\n        }\n    }\n}(jQuery));\n\n(function ($) {\n    $.gimmicks = $.fn.gimmicks = function (method) {\n        if (method === undefined) {\n            return;\n        }\n        // call the gimmick\n        if ($.fn.gimmicks.methods[method]) {\n            return $.fn.gimmicks.methods[method].apply(this, Array.prototype.slice.call(arguments, 1));\n        } else {\n            $.error('Gimmick ' + method + ' does not exist on jQuery.gimmicks');\n        }\n    };\n\n    // TODO underscores _ in Markdown links are not allowed! bug in our MD imlemenation\n\n\n}(jQuery));\n\n(function ($) {\n    //'use strict';\n    var alertsGimmick = {\n        name: 'alerts',\n        // TODO\n        //version: $.md.version,\n        load: function () {\n            $.md.stage('bootstrap').subscribe(function (done) {\n                createAlerts();\n                done();\n            });\n        }\n    };\n    $.md.registerGimmick(alertsGimmick);\n\n    // takes a standard <img> tag and adds a hyperlink to the image source\n    // needed since we scale down images via css and want them to be accessible\n    // in original format\n    function createAlerts() {\n        var matches = $(select_paragraphs());\n        matches.each(function () {\n            var $p = $(this.p);\n            var type = this.alertType;\n            $p.addClass('alert');\n\n            if (type === 'note') {\n                $p.addClass('alert-info');\n            } else if (type === 'hint') {\n                $p.addClass('alert-success');\n            } else if (type === 'warning') {\n                $p.addClass('alert-warning');\n            }\n        });\n    }\n\n    // picks out the paragraphs that start with a trigger word\n    function select_paragraphs() {\n        var note = ['note', 'beachte'];\n        var warning = ['achtung', 'attention', 'warnung', 'warning', 'atención', 'guarda', 'advertimiento'];\n        var hint = ['hint', 'tipp', 'tip', 'hinweis'];\n        var exp = note.concat(warning);\n        exp = exp.concat(hint);\n        var matches = [];\n\n        $('p').filter(function () {\n            var $par = $(this);\n            // check against each expression\n            $(exp).each(function (i, trigger) {\n                var txt = $par.text().toLowerCase();\n                // we match only paragrachps in which the 'trigger' expression\n                // is follow by a ! or :\n                var re = new RegExp(trigger + '(:|!)+.*', 'i');\n                var alertType = 'none';\n                if (txt.match(re) !== null) {\n                    if ($.inArray(trigger, note) >= 0) {\n                        alertType = 'note';\n                    } else if ($.inArray(trigger, warning) >= 0) {\n                        alertType = 'warning';\n                    } else if ($.inArray(trigger, hint) >= 0) {\n                        alertType = 'hint';\n                    }\n                    matches.push({\n                        p: $par,\n                        alertType: alertType\n                    });\n                }\n            });\n        });\n        return matches;\n    }\n}(jQuery));\n\n(function ($) {\n    // makes trouble, find out why\n    //'use strict';\n    var colorboxGimmick = {\n        name: 'colorbox',\n        load: function () {\n            $.md.stage('gimmick').subscribe(function (done) {\n                $.gimmicks('colorbox');\n                done();\n            });\n        }\n    };\n    $.md.registerGimmick(colorboxGimmick);\n\n    var methods = {\n        // takes a standard <img> tag and adds a hyperlink to the image source\n        // needed since we scale down images via css and want them to be accessible\n        // in original format\n        colorbox: function () {\n            var $image_groups;\n            if (!(this instanceof jQuery)) {\n                // select the image groups of the page\n                $image_groups = $('.md-image-group');\n            } else {\n                $image_groups = $(this);\n            }\n            // operate on md-image-group, which holds one\n            // or more images that are to be colorbox'ed\n            var counter = 0;\n            return $image_groups.each(function () {\n                var $this = $(this);\n\n                // each group requires a unique name\n                var gal_group = 'gallery-group-' + (counter++);\n\n                // create a hyperlink around the image\n                $this.find('a.md-image-selfref img')\n                    // filter out images that already are a hyperlink\n                    // (so won't be part of the gallery)\n\n                    // apply colorbox on their parent anchors\n                    .parents('a').colorbox({\n                        rel: gal_group,\n                        opacity: 0.75,\n                        slideshow: true,\n                        maxWidth: '95%',\n                        maxHeight: '95%',\n                        scalePhotos: true,\n                        photo: true,\n                        slideshowAuto: false\n                    });\n            });\n        }\n    };\n    $.gimmicks.methods = $.extend({}, $.fn.gimmicks.methods, methods);\n}(jQuery));\n\n(function ($) {\n    'use strict';\n\n    var themeChooserGimmick = {\n        name: 'Themes',\n        version: $.md.version,\n        once: function () {\n            $.md.linkGimmick(this, 'carousel', carousel);\n        }\n    };\n    $.md.registerGimmick(themeChooserGimmick);\n\n    function carousel($link, opt, href) {\n\n        var $c = $('<div id=\"myCarousel\" class=\"carousel slide\"></div>');\n        var $d = $('<div class=\"carousel-inner\"/>');\n        $c.append('<ol class=\"carousel-indicators\" />');\n\n        var imageUrls = [];\n        var i = 0;\n        $.each(href.split(','), function (i, e) {\n            imageUrls.push($.trim(e));\n            $c.find('ol').append('<li data-target=\"#myCarousel\" data-slide-to=\"' + i + '\" class=\"active\" /');\n            var div;\n            if (i === 0) {\n                div = ('<div class=\"active item\"/>');\n            } else {\n                div = ('<div class=\"item\"/>');\n            }\n            $d.append($(div).append('<img src=\"' + e + '\"/>'));\n        });\n        $c.append($d);\n        $c.append('<a class=\"carousel-control left\" href=\"#myCarousel\" data-slide=\"prev\">&lsaquo;</a>');\n        $c.append('<a class=\"carousel-control right\" href=\"#myCarousel\" data-slide=\"next\">&rsaquo;</a>');\n        $link.replaceWith($c);\n    }\n}(jQuery));\n\n(function ($) {\n    var disqusGimmick = {\n        name: 'disqus',\n        version: $.md.version,\n        once: function () {\n            $.md.linkGimmick(this, 'disqus', disqus);\n        }\n    };\n    $.md.registerGimmick(disqusGimmick);\n\n    var alreadyDone = false;\n    var disqus = function ($links, opt, text) {\n        var default_options = {\n            identifier: ''\n        };\n        var options = $.extend(default_options, opt);\n        var disqus_div = $('<div id=\"disqus_thread\" class=\"md-external md-external-noheight md-external-nowidth\" >' + '<a href=\"http://disqus.com\" class=\"dsq-brlink\">comments powered by <span class=\"logo-disqus\">Disqus</span></a></div>');\n        disqus_div.css('margin-top', '2em');\n        return $links.each(function (i, link) {\n            if (alreadyDone === true) {\n                return;\n            }\n            alreadyDone = true;\n\n            var $link = $(link);\n            var disqus_shortname = $link.attr('href');\n\n            if (disqus_shortname !== undefined && disqus_shortname.length > 0) {\n                // insert the div\n                $link.remove();\n                // since disqus need lot of height, always but it on the bottom of the page\n                $('#md-content').append(disqus_div);\n                if ($('#disqus_thread').length > 0) {\n                    (function () {\n                        // all disqus_ variables are used by the script, they\n                        // change the config behavious.\n                        // see: http://help.disqus.com/customer/portal/articles/472098-javascript-configuration-variables\n\n                        // set to 1 if developing, or the site is password protected or not\n                        // publicaly accessible\n                        //var disqus_developer = 1;\n\n                        // by default, disqus will use the current url to determine a thread\n                        // since we might have different parameters present, we remove them\n                        // disqus_* vars HAVE TO BE IN GLOBAL SCOPE\n                        var disqus_url = window.location.href;\n                        var disqus_identifier;\n                        if (options.identifier.length > 0) {\n                            disqus_identifier = options.identifier;\n                        } else {\n                            disqus_identifier = disqus_url;\n                        }\n\n                        // dynamically load the disqus script\n                        var dsq = document.createElement('script');\n                        dsq.type = 'text/javascript';\n                        dsq.async = true;\n                        dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';\n                        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);\n                    })();\n                }\n            }\n        });\n    };\n}(jQuery));\n\n(function ($) {\n    var language = window.navigator.userLanguage || window.navigator.language;\n    var code = language + \"_\" + language.toUpperCase();\n    var fbRootDiv = $('<div id=\"fb-root\" />');\n    var fbScriptHref = $.md.prepareLink('connect.facebook.net/' + code + '/all.js#xfbml=1', { forceHTTP: true });\n    var fbscript = '(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = \"' + fbScriptHref + '\"; fjs.parentNode.insertBefore(js, fjs);}(document, \"script\", \"facebook-jssdk\"));';\n\n    var facebookLikeGimmick = {\n        name: 'FacebookLike',\n        version: $.md.version,\n        once: function () {\n            $.md.linkGimmick(this, 'facebooklike', facebooklike);\n            $.md.registerScript(this, fbscript, {\n                license: 'APACHE2',\n                loadstage: 'postgimmick',\n                finishstage: 'all_ready'\n            });\n        }\n    };\n    $.md.registerGimmick(facebookLikeGimmick);\n\n    function facebooklike($link, opt, text) {\n        var default_options = {\n            layout: 'standard',\n            showfaces: true\n        };\n        var options = $.extend({}, default_options, opt);\n        // Due to a bug, we can have underscores _ in a markdown link\n        // so we insert the underscores needed by facebook here\n        if (options.layout === 'boxcount') {\n            options.layout = 'box_count';\n        }\n        if (options.layout === 'buttoncount') {\n            options.layout = 'button_count';\n        }\n\n        return $link.each(function (i, e) {\n            var $this = $(e);\n            var href = $this.attr('href');\n            $('body').append(fbRootDiv);\n\n            var $fb_div = $('<div class=\"fb-like\" data-send=\"false\" data-width=\"450\"></div>');\n            $fb_div.attr('data-href', href);\n            $fb_div.attr('data-layout', options.layout);\n            $fb_div.attr('data-show-faces', options.showfaces);\n\n            $this.replaceWith($fb_div);\n        });\n    }\n}(jQuery));\n\n(function ($) {\n    'use strict';\n    var forkmeongithubGimmick = {\n        name: 'forkmeongithub',\n        version: $.md.version,\n        once: function () {\n            $.md.linkGimmick(this, 'forkmeongithub', forkmeongithub);\n        }\n    };\n    $.md.registerGimmick(forkmeongithubGimmick);\n\n    function forkmeongithub($links, opt, text) {\n        return $links.each(function (i, link) {\n            var $link = $(link);\n            // default options\n            var default_options = {\n                color: 'red',\n                position: 'right'\n            };\n            var options = $.extend({}, default_options, opt);\n            var color = options.color;\n            var pos = options.position;\n\n            // the filename for the ribbon\n            // see: https://github.com/blog/273-github-ribbons\n            var base_href = 'https://s3.amazonaws.com/github/ribbons/forkme_';\n\n            if (color === 'red') {\n                base_href += pos + '_red_aa0000.png';\n            }\n            if (color === 'green') {\n                base_href += pos + '_green_007200.png';\n            }\n            if (color === 'darkblue') {\n                base_href += pos + '_darkblue_121621.png';\n            }\n            if (color === 'orange') {\n                base_href += pos + '_orange_ff7600.png';\n            }\n            if (color === 'white') {\n                base_href += pos + '_white_ffffff.png';\n            }\n            if (color === 'gray') {\n                base_href += pos + '_gray_6d6d6d.png';\n            }\n\n            var href = $link.attr('href');\n            //                var body_pos_top = $('#md-body').offset ().top;\n            var body_pos_top = 0;\n            var github_link = $('<a class=\"forkmeongithub\" href=\"' + href + '\"><img style=\"position: absolute; top: ' + body_pos_top + ';' + pos + ': 0; border: 0;\" src=\"' + base_href + '\" alt=\"Fork me on GitHub\"></a>');\n            // to avoid interfering with other div / scripts, we remove the link and prepend it to the body\n            // the fork me ribbon is positioned absolute anyways\n            $('body').prepend(github_link);\n            github_link.find('img').css('z-index', '2000');\n            $link.remove();\n        });\n    }\n\n}(jQuery));\n\n(function ($) {\n    'use strict';\n    var gistGimmick = {\n        name: 'gist',\n        once: function () {\n            $.md.linkGimmick(this, 'gist', gist);\n        }\n    };\n    $.md.registerGimmick(gistGimmick);\n\n    function gist($links, opt, href) {\n        $().lazygist('init');\n        return $links.each(function (i, link) {\n            var $link = $(link);\n            var gistDiv = $('<div class=\"gist_here\" data-id=\"' + href + '\" />');\n            $link.replaceWith(gistDiv);\n            gistDiv.lazygist({\n                // we dont want a specific file so modify the url template\n                url_template: 'https://gist.github.com/{id}.js?'\n            });\n        });\n    }\n}(jQuery));"
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/Views/dashboard.js",
    "content": "var table = null;\n$(function () {\n    table = $('#tradingPairsTable').DataTable({\n        ajax: {\n            url: \"TradingPairs\",\n            type: \"POST\",\n            dataSrc: \"\"\n        },\n        columns: [\n            {\n                className: 'control',\n                orderable: false,\n                data: null,\n                defaultContent: ''\n            },\n            {\n                name: \"Name\",\n                data: \"Name\",\n                render: function (data, type, row, meta) {\n                    return '<a href=\"https://www.tradingview.com/chart/?symbol=' + row.TradingViewName + '\" target = \"_blank\" class=\"btn btn-outline-info btn-sm\">' + data + '</a>';\n                },\n                visible: false\n            },\n            {\n                name: \"FormattedName\",\n                data: \"Name\",\n                render: function (data, type, row, meta) {\n                    var element = '<div style=\"width: 120px\"><a href=\"https://www.tradingview.com/chart/?symbol=' + row.TradingViewName + '\" target = \"_blank\" class=\"btn btn-outline-info btn-sm\">' + data + '</a>';\n                    if (row.DCA > 0) {\n                        element += '&nbsp;&nbsp;<span class=\"badge badge-primary\" title=\"DCA level\">' + row.DCA + '</span>';\n                    }\n                    element += '</div>';\n                    return element;\n                }\n            },\n            {\n                name: \"DCA\",\n                data: \"DCA\",\n                visible: false\n            },\n            {\n                name: \"Margin\",\n                data: \"Margin\",\n                render: function (data, type, row, meta) {\n                    var element = \"\";\n                    if (parseFloat(data) >= 0) {\n                        element = '<span class=\"text-success\"><strong>' + data + '</strong></span>';\n                    }\n                    else {\n                        element = '<span class=\"text-warning\"><strong>' + data + '</strong></span>';\n                    }\n                    if (row.IsTrailingSell) {\n                        element += ' <i class=\"fas fa-bolt text-info\" title=\"Trailing\"></i>';\n                    }\n                    if (row.IsTrailingBuy) {\n                        element += ' <i class=\"fas fa-bolt text-primary\" title=\"Trailing\"></i>';\n                    }\n                    return element;\n                }\n            },\n            {\n                name: \"Target\",\n                data: \"Target\"\n            },\n            {\n                name: \"CurrentRating\",\n                data: \"CurrentRating\",\n                render: function (data, type, row, meta) {\n                    var element = \"\";\n                    if (parseFloat(data) >= parseFloat(row.BoughtRating)) {\n                        element = '<span class=\"text-success\">' + data + '</span>';\n                    }\n                    else {\n                        element = '<span class=\"text-warning\">' + data + '</span>';\n                    }\n                    return element;\n                }\n            },\n            {\n                name: \"BoughtRating\",\n                data: \"BoughtRating\"\n            },\n            {\n                name: \"Age\",\n                data: \"Age\"\n            },\n            {\n                name: \"Amount\",\n                data: \"Amount\"\n            },\n            {\n                name: \"CurrentCost\",\n                data: \"CurrentCost\"\n            },\n            {\n                name: \"Cost\",\n                data: \"Cost\"\n            },\n            {\n                name: \"CurrentPrice\",\n                data: \"CurrentPrice\"\n            },\n            {\n                name: \"BoughtPrice\",\n                data: \"BoughtPrice\"\n            },\n            {\n                name: \"CurrentSpread\",\n                data: \"CurrentSpread\"\n            },\n            {\n                name: \"SignalRule\",\n                data: \"SignalRule\"\n            },\n            {\n                Name: \"TradingRules\",\n                data: \"TradingRules\"\n            },\n            {\n                name: \"OrderDates\",\n                data: \"OrderDates\",\n                visible: false\n            },\n            {\n                name: \"OrderIds\",\n                data: \"OrderIds\",\n                visible: false\n            }\n        ],\n        order: [[4, \"desc\"]],\n        responsive: {\n            details: {\n                type: \"column\"\n            }\n        },\n        paging: false,\n        colReorder: true,\n        stateSave: true,\n        dom: 'Bfrtp',\n        buttons: [\n            {\n                extend: \"colvis\",\n                text: \"Columns\"\n            },\n            \"copy\",\n            \"csv\",\n            {\n                text: 'Log',\n                action: function (e, dt, node, config) {\n                    $('#logEntries').collapse('toggle');\n                }\n            }\n        ],\n        footerCallback: function (row, data, start, end, display) {\n            $(this.api().column(\"Name:name\").footer()).html(\"Total: \" + this.api().column(\"Name:name\").data().length);\n            $(this.api().column(\"FormattedName:name\").footer()).html(\"Total: \" + this.api().column(\"FormattedName:name\").data().length);\n            $(this.api().column(\"Margin:name\").footer()).html(\"Avg: \" + this.api().column(\"Margin:name\").data().average().toFixed(2));\n            $(this.api().column(\"Cost:name\").footer()).html(\"Total: \" + this.api().column(\"Cost:name\").data().sum().toFixed(8));\n            $(this.api().column(\"CurrentCost:name\").footer()).html(\"Total: \" + this.api().column(\"CurrentCost:name\").data().sum().toFixed(8));\n            $(this.api().column(\"Age:name\").footer()).html(\"Avg: \" + this.api().column(\"Age:name\").data().average().toFixed(2));\n            $(this.api().column(\"CurrentRating:name\").footer()).html(\"Avg: \" + this.api().column(\"CurrentRating:name\").data().average().toFixed(3));\n            $(this.api().column(\"BoughtRating:name\").footer()).html(\"Avg: \" + this.api().column(\"BoughtRating:name\").data().average().toFixed(3));\n        }\n    });\n\n    $('#tradingPairsTable tbody').on('click', 'td:not(:first-child)', function (ev) {\n        if (ev.target.tagName === \"A\")\n            return;\n        var tr = $(this).closest('tr');\n        var row = table.row(tr);\n        if (row.child.isShown()) {\n            hideRow(row);\n        }\n        else {\n            showRow(row);\n        }\n    });\n\n    setInterval(function () {\n        refreshTable();\n    }, 5000);\n\n    document.addEventListener(\"visibilitychange\", function () {\n        refreshTable();\n    }, false);\n});\n\nfunction refreshTable() {\n    if (!document.hidden && $(\".additional-details\").length == 0 && $(\".dtr-details\").length == 0) {\n        table.ajax.reload(null, false);\n    }\n}\n\nfunction showRow(row) {\n    row.child(format(row.data())).show();\n    $(row.node()).addClass('shown');\n}\n\nfunction hideRow(row) {\n    row.child.hide();\n    $(row.node()).removeClass('shown');\n}\n\nfunction format(data) {\n    var details = $($(\"#rowDetails\").html());\n    details.find(\"#pair\").val(data.Name);\n    details.find(\"#amount\").attr(\"value\", data.Amount).attr(\"min\", 0);\n\n    var swapPairContainer = details.find(\"#swapPairContainer\");\n    if (data.SwapPair) {\n        swapPairContainer.show();\n        details.find(\"#swapPair\").text(data.SwapPair);\n    } else {\n        swapPairContainer.hide();\n    }\n\n    details.find(\"#signalRule\").text(data.SignalRule);\n    details.find(\"#tradingRules\").text(data.TradingRules.join(\", \"));\n    details.find(\"#orderDates\").text(data.OrderDates.join(\", \"));\n    details.find(\"#orderIds\").text(data.OrderIds.join(\", \"));\n    details.find(\"#lastBuyMargin\").text(data.LastBuyMargin);\n    return details.html();\n}\n\nfunction showSettings(e) {\n    var pair = $(e).closest(\".row-details\").find(\"#pair\").val();\n    var tr = $(e).closest('tr').prev();\n    var row = table.row(tr);\n    var config = row.data().Config;\n    $(\"#modalTitle\").text(pair + \" Settings\");\n    $(\"#modalContent\").html(\"<pre>\" + JSON.stringify(config, null, 4) + \"</pre>\");\n    $(\"#modal\").modal('show');\n}\n\nfunction sellPair(e) {\n    var pair = $(e).closest(\".row-details\").find(\"#pair\").val();\n    var amount = $(e).parent().find(\"#amount\").val();\n    if (confirm(\"Sell \" + amount + \" \" + pair + \"?\")) {\n        $.post(\"Sell\", { pair: pair, amount: amount }, function (data) {\n            var tr = $(e).closest('tr').prev();\n            var row = table.row(tr);\n            hideRow(row);\n            refreshTable();\n        }).fail(function (data) {\n            alert(\"Error selling \" + pair);\n        });\n    }\n}\n\nfunction buyPair(e) {\n    var pair = $(e).closest(\".row-details\").find(\"#pair\").val();\n    var amount = $(e).parent().find(\"#amount\").val();\n    if (confirm(\"Buy \" + amount + \" \" + pair + \"?\")) {\n        $.post(\"Buy\", { pair: pair, amount: amount }, function (data) {\n            var tr = $(e).closest('tr').prev();\n            var row = table.row(tr);\n            hideRow(row);\n            refreshTable();\n        }).fail(function (data) {\n            alert(\"Error buying \" + pair);\n        });\n    }\n}\n\nfunction swapPair(e) {\n    var pair = $(e).closest(\".row-details\").find(\"#pair\").val();\n    var swap = prompt(\"Enter a pair to swap \" + pair + \" for\");\n    if (swap) {\n        $.post(\"Swap\", { pair: pair, swap: swap }, function (data) {\n            var tr = $(e).closest('tr').prev();\n            var row = table.row(tr);\n            hideRow(row);\n            refreshTable();\n        }).fail(function (data) {\n            alert(\"Error swapping \" + pair);\n        });\n    }\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/Views/market.js",
    "content": "var table = null;\n$(function () {\n    table = $('#marketPairsTable').DataTable({\n        ajax: {\n            url: \"MarketPairs\",\n            data: function (d) {\n                var signalNames = $('#signalsFilter').find(\":selected\").map(function () {\n                    return $.trim($(this).text());\n                }).get();\n\n                if (signalNames.length) {\n                    d.signalsFilter = signalNames;\n                } else {\n                    d.signalsFilter = [];\n                }\n            },\n            type: \"POST\",\n            dataSrc: \"\"\n        },\n        columns: [\n            {\n                className: 'control',\n                orderable: false,\n                data: null,\n                defaultContent: ''\n            },\n            {\n                name: \"Name\",\n                data: \"Name\",\n                render: function (data, type, row, meta) {\n                    var outlineStyle = row.HasTradingPair ? \"info\" : \"secondary\";\n                    var element = '<div style=\"width: 120px\"><a href=\"https://www.tradingview.com/chart/?symbol=' + row.TradingViewName + '\" target = \"_blank\" class=\"btn btn-outline-' + outlineStyle + ' btn-sm\">' + data + '</a>';\n                    if (row.SignalRules.length) {\n                        element += '&nbsp;<i class=\"fas fa-bolt text-info\" title=\"Trailing\"></i>';\n                    }\n                    element += '</div>';\n                    return element;\n                }\n            },\n            {\n                name: \"Rating\",\n                data: \"RatingList\",\n                type: \"multi-value\",\n                render: function (data, type, row, meta) {\n                    return data.map(function (item) { return '<div class=\"signal-details\"><span class=\"signal-name\">' + item.Name + '</span><span class=\"signal-value ' + (item.Rating != null && item.Rating >= 0 ? 'text-success' : 'text-warning') + '\">' + (item.Rating != null ? item.Rating.toFixed(3) : \"N/A\") + '</span></div>'; }).join(\"\");\n                }\n            },\n            {\n                name: \"RatingChange\",\n                data: \"RatingChangeList\",\n                type: \"multi-value\",\n                render: function (data, type, row, meta) {\n                    return data.map(function (item) { return '<div class=\"signal-details\"><span class=\"signal-name\">' + item.Name + '</span><span class=\"signal-value ' + (item.RatingChange != null && item.RatingChange >= 0 ? 'text-success' : 'text-warning') + '\">' + (item.RatingChange != null ? item.RatingChange.toFixed(2) : \"N/A\") + '</span></div>'; }).join(\"\");\n                },\n                visible: false\n            },\n            {\n                name: \"Price\",\n                data: \"Price\",\n                render: function (data, type, row, meta) {\n                    return data;\n                }\n            },\n            {\n                name: \"PriceChange\",\n                data: \"PriceChangeList\",\n                type: \"multi-value\",\n                render: function (data, type, row, meta) {\n                    return data.map(function (item) { return '<div class=\"signal-details\"><span class=\"signal-name\">' + item.Name + '</span><span class=\"signal-value ' + (item.PriceChange != null && item.PriceChange >= 0 ? 'text-success' : 'text-warning') + '\">' + (item.PriceChange != null ? item.PriceChange.toFixed(2) : \"N/A\") + '</span></div>'; }).join(\"\");\n                }\n            },\n            {\n                name: \"Spread\",\n                data: \"Spread\",\n                render: function (data, type, row, meta) {\n                    return data;\n                }\n            },\n            {\n                name: \"Arbitrage\",\n                data: \"ArbitrageList\",\n                type: \"multi-value\",\n                render: function (data, type, row, meta) {\n                    return data.map(function (item) { return '<div class=\"signal-details\"><span class=\"signal-name\">' + item.Name + '</span><span class=\"signal-value\">' + (item.Arbitrage != null ? item.Arbitrage : \"N/A\") + '</span></div>'; }).join(\"\");\n                }\n            },\n            {\n                name: \"Volume\",\n                data: \"VolumeList\",\n                type: \"multi-value\",\n                render: function (data, type, row, meta) {\n                    return data.map(function (item) { return '<div class=\"signal-details\"><span class=\"signal-name\">' + item.Name + '</span><span class=\"signal-value\">' + (item.Volume != null ? item.Volume : \"N/A\") + '</span></div>'; }).join(\"\");\n                }\n            },\n            {\n                name: \"VolumeChange\",\n                data: \"VolumeChangeList\",\n                type: \"multi-value\",\n                render: function (data, type, row, meta) {\n                    return data.map(function (item) { return '<div class=\"signal-details\"><span class=\"signal-name\">' + item.Name + '</span><span class=\"signal-value ' + (item.VolumeChange != null && item.VolumeChange >= 0 ? 'text-success' : 'text-warning') + '\">' + (item.VolumeChange != null ? item.VolumeChange.toFixed(2) : \"N/A\") + '</span></div>'; }).join(\"\");\n                },\n                visible: false\n            },\n            {\n                name: \"Volatility\",\n                data: \"VolatilityList\",\n                type: \"multi-value\",\n                render: function (data, type, row, meta) {\n                    return data.map(function (item) { return '<div class=\"signal-details\"><span class=\"signal-name\">' + item.Name + '</span><span class=\"signal-value\">' + (item.Volatility != null ? item.Volatility.toFixed(2) : \"N/A\") + '</span></div>'; }).join(\"\");\n                }\n            },\n            {\n                name: \"TradingRules\",\n                data: \"Config\",\n                render: function (data, type, row, meta) {\n                    return data.Rules.join(\"<br/>\");\n                }\n            },\n            {\n                name: \"SignalRules\",\n                data: \"SignalRules\",\n                render: function (data, type, row, meta) {\n                    return data.join(\"<br/>\");\n                }\n            }\n        ],\n        order: [[2, \"desc\"]],\n        responsive: {\n            details: {\n                type: \"column\"\n            }\n        },\n        pageLength: 25,\n        colReorder: true,\n        stateSave: true,\n        dom: 'Bfrtp',\n        buttons: [\n            {\n                extend: \"colvis\",\n                text: \"Columns\"\n            },\n            \"copy\",\n            \"csv\",\n            {\n                text: 'Log',\n                action: function (e, dt, node, config) {\n                    $('#logEntries').collapse('toggle');\n                }\n            }\n        ]\n    });\n\n    table.search();\n\n    $.fn.dataTable.ext.type.order['multi-value-pre'] = function (d) {\n        return getMultiValueAvg(d);\n    };\n\n    $.fn.dataTable.ext.search.push(\n        function (settings, searchData, dataIndex) {\n            var show = true;\n            $(\".filter\").each(function (idx, element) {\n                var filter = $(element);\n                var value = parseFloat(filter.val());\n                var valueIndex = filter.data(\"index\");\n                if (value && valueIndex) {\n                    var visibleData = [];\n                    for (var idx in searchData) {\n                        if (settings.aoColumns.filter(function (col) { return col.idx == idx; })[0].bVisible) {\n                            visibleData.push(searchData[idx]);\n                        }\n                    }\n\n                    var data = visibleData[valueIndex + 1];\n                    if (!isNaN(data)) {\n                        if (parseFloat(data) < value) {\n                            show = false;\n                        }\n                    } else {\n                        var sum = getMultiValueAvg(data);\n                        if (sum < value) {\n                            show = false;\n                        }\n                    }\n                }\n            });\n            return show;\n        }\n    );\n\n    $('#marketPairsTable thead th:not(.control)').each(function (i) {\n        var title = $('#marketPairsTable thead th').eq($(this).index()).text();\n        if (title != \"Name\" && title != \"Trading Rules\" && title != \"Trailing Rules\" && title != \"Signal Rules\") {\n            $(this).prepend('<input type=\"text\" class=\"filter\" onclick=\"return filterClicked(event);\" placeholder=\"Min ' + title + '\" data-index=\"' + i + '\" />');\n        }\n    });\n\n    $(table.table().container()).on('keyup', 'thead input', function () {\n        table.draw();\n    });\n\n    $('#marketPairsTable tbody').on('click', 'td:not(:first-child)', function (ev) {\n        if (ev.target.tagName === \"A\")\n            return;\n        var tr = $(this).closest('tr');\n        var row = table.row(tr);\n        if (row.child.isShown()) {\n            hideRow(row);\n        }\n        else {\n            showRow(row);\n        }\n    });\n\n    function getMultiValueAvg(element) {\n        var total = 0;\n        var sum = 0;\n        $(element).find(\".signal-value\").each(function (idx, element) {\n            var value = parseFloat(element.innerText);\n            if (!value) value = 0;\n            sum += value;\n            total++;\n        });\n        return sum / total;\n    }\n\n    setInterval(function () {\n        refreshTable();\n    }, 5000);\n\n    document.addEventListener(\"visibilitychange\", function () {\n        refreshTable();\n    }, false);\n\n    $.get(\"/SignalNames\", function (data) {\n        $('<div class=\"signals-filter\"><select id=\"signalsFilter\" multiple=\"multiple\"></div>').insertAfter(\".dt-buttons\");\n        var signalsFilter = $(\"#signalsFilter\");\n        for (var i = 0; i < data.length; i++) {\n            var signalName = data[i];\n            signalsFilter.append('<option selected=\"selected\">' + signalName + '</option>');\n        }\n        signalsFilter.multiselect({\n            buttonText: function () {\n                return \"Signals\";\n            },\n            optionClass: function () {\n                return \"signal-filter-option\";\n            },\n            onChange: function () {\n                refreshTable();\n            }\n        });\n    });\n});\n\nfunction refreshTable() {\n    if (!document.hidden && $(\".additional-details\").length == 0 && $(\".dtr-details\").length == 0) {\n        table.ajax.reload(null, false);\n    }\n}\n\nfunction showRow(row) {\n    row.child(format(row.data())).show();\n    $(row.node()).addClass('shown');\n}\n\nfunction hideRow(row) {\n    row.child.hide();\n    $(row.node()).removeClass('shown');\n}\n\nfunction format(data) {\n    var details = $($(\"#rowDetails\").html());\n    details.find(\"#pair\").val(data.Name);\n    details.find(\"#amount\").attr(\"value\", data.Amount).attr(\"min\", 0);\n    details.find(\"#signalRules\").text(data.SignalRules.join(\", \"));\n    details.find(\"#tradingRules\").text(data.Config.Rules.join(\", \"));\n    return details.html();\n}\n\nfunction filterClicked(e) {\n    e.preventDefault();\n    e.stopPropagation();\n    return false;\n}\n\nfunction showSettings(e) {\n    var pair = $(e).closest(\".row-details\").find(\"#pair\").val();\n    var tr = $(e).closest('tr').prev();\n    var row = table.row(tr);\n    var config = row.data().Config;\n    $(\"#modalTitle\").text(pair + \" Settings\");\n    $(\"#modalContent\").html(\"<pre>\" + JSON.stringify(config, null, 4) + \"</pre>\");\n    $(\"#modal\").modal('show');\n}\n\nfunction buyPair(e) {\n    var pair = $(e).closest(\".row-details\").find(\"#pair\").val();\n    var amount = $(e).parent().find(\"#amount\").val();\n    if (confirm(\"Buy \" + amount + \" \" + pair + \"?\")) {\n        $.post(\"Buy\", { pair: pair, amount: amount }, function (data) {\n            var tr = $(e).closest('tr').prev();\n            var row = table.row(tr);\n            hideRow(row);\n            refreshTable();\n        }).fail(function (data) {\n            alert(\"Error buying \" + pair);\n        });\n    }\n}\n\nfunction buyPairDefault(e) {\n    var pair = $(e).closest(\".row-details\").find(\"#pair\").val();\n    var amount = $(e).parent().find(\"#amount\").val();\n    if (confirm(\"Buy \" + pair + \" with default settings?\")) {\n        $.post(\"BuyDefault\", { pair: pair }, function (data) {\n            var tr = $(e).closest('tr').prev();\n            var row = table.row(tr);\n            hideRow(row);\n            refreshTable();\n        }).fail(function (data) {\n            alert(\"Error buying \" + pair);\n        });\n    }\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/Views/rules.js",
    "content": "﻿var table = null;\n$(function () {\n    table = $('#rulesTable').DataTable({\n        pageLength: 100,\n        responsive: true,\n        colReorder: true,\n        stateSave: true,\n        dom: 'Bflrtip',\n        buttons: [\n            {\n                extend: \"colvis\",\n                text: \"Columns\"\n            },\n            \"copy\",\n            \"csv\"\n        ],\n        order: [[1, \"desc\"]]\n    });\n});"
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/Views/settings.js",
    "content": "var editors = {};\n\n$(function () {\n    $(\".config-container\").each(function (idx, element) {\n        var json = $(element).find(\".config-value\").text();\n        var container = $(element).find(\".config-editor\");\n        var configName = container.data(\"config\");\n        try {\n            var options = {\n                mode: 'code',\n                modes: ['code', 'tree', 'form', 'text', 'view'],\n                onModeChange: function (newMode, oldMode) {\n                    if (newMode == \"code\") {\n                        editor.editor.setOptions({ maxLines: Infinity });\n                    }\n                }\n            };\n            var editor = new JSONEditor(container[0], options);\n            editor.editor.setOptions({ maxLines: Infinity });\n            editor.setText(json);\n            editors[configName] = editor;\n        } catch (ex) {\n            container.html('<span class=\"text-danger\">Editing not available - invalid json</span>');\n        }\n    });\n});\n\nfunction refreshAccount() {\n    if (confirm(\"Refresh Account?\")) {\n        $.get(\"/RefreshAccount\", function (data) {\n\n        }).fail(function (data) {\n            alert(\"Unable to refresh account\");\n        });\n    }\n}\n\nfunction restartServices() {\n    if (confirm(\"Restart Services?\")) {\n        $.get(\"/RestartServices\", function (data) {\n\n        }).fail(function (data) {\n            //alert(\"Unable to restart services\");\n        });\n    }\n}\n\nfunction logout() {\n    if (confirm(\"Log out?\")) {\n        window.location.href = \"/Logout\";\n    }\n}\n\nfunction saveConfig(e) {\n    var tab = $(e).closest(\".tab-pane\");\n    var configName = tab.find(\".config-editor\").data(\"config\");\n    var editor = editors[configName];\n    var json = JSON.stringify(editor.get(), null, 2);\n    var configStatus = tab.find(\"#saveConfigStatus\");\n    configStatus.text(\"Saving...\");\n    $.post(\"/SaveConfig\", { name: configName, definition: json }, function (data) {\n        configStatus.text(\"Saved\");\n        setTimeout(function () {\n            configStatus.text(\"\");\n        }, 1000);\n    }).fail(function (data) {\n        alert(\"Unable to save configuration\");\n    });\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/Views/stats.js",
    "content": "var table = null;\n$(function () {\n    table = $('#statsTable').DataTable({\n        pageLength: 25,\n        responsive: true,\n        colReorder: true,\n        stateSave: true,\n        dom: 'Blrtip',\n        buttons: [\n            {\n                extend: \"colvis\",\n                text: \"Columns\"\n            },\n            \"copy\",\n            \"csv\"\n        ],\n        order: [[0, \"desc\"]]\n    });\n\n    $('<div class=\"rules-analyzer\"><button class=\"btn btn-success\" onclick=\"showRulesAnalyzer();\">Rules Analyzer</button></div>').insertAfter(\".dt-buttons\");\n});\n\nfunction showRulesAnalyzer() {\n    window.location = \"/Rules\";\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/Views/trades.js",
    "content": "var table = null;\n$(function () {\n    table = $('#tradesTable').DataTable({\n        pageLength: 100,\n        responsive: true,\n        colReorder: true,\n        stateSave: true,\n        dom: 'Bflrtip',\n        buttons: [\n            {\n                extend: \"colvis\",\n                text: \"Columns\"\n            },\n            \"copy\",\n            \"csv\"\n        ],\n        order: [[0, \"desc\"]]\n    });\n});"
  },
  {
    "path": "IntelliTrader.Web/Static/Scripts/intellitrader.js",
    "content": "$(function () {\n    if (window.isAuthenticated) {\n        setInterval(function () {\n            updateStatus();\n        }, 5000);\n        setStatus(\"none\");\n        updateStatus();\n        document.addEventListener(\"visibilitychange\", function () {\n            updateStatus();\n        }, false);\n    }\n});\n\nfunction updateStatus() {\n    if (document.hidden)\n        return;\n    setStatus(\"refreshing\");\n    $.get(\"/Status\", function (data) {\n        var accountBalance = $(\"#accountBalance\");\n        accountBalance.text(data.Balance.toFixed(8));\n        var globalRating = $(\"#globalRating\");\n        globalRating.text(data.GlobalRating);\n        if (parseFloat(data.GlobalRating) > 0) {\n            globalRating.removeClass(\"text-warning\");\n            globalRating.addClass(\"text-success\");\n        }\n        else {\n            globalRating.removeClass(\"text-success\");\n            globalRating.addClass(\"text-warning\");\n        }\n        var trailingBuys = $(\"#trailingBuys\");\n        trailingBuys.text(data.TrailingBuys.length);\n        trailingBuys.attr(\"title\", \"Buys:\\r\\n\" + data.TrailingBuys.join(\"\\r\\n\"));\n        var trailingSells = $(\"#trailingSells\");\n        trailingSells.text(data.TrailingSells.length);\n        trailingSells.attr(\"title\", \"Sells:\\r\\n\" + data.TrailingSells.join(\"\\r\\n\"));\n        var trailingSignals = $(\"#trailingSignals\");\n        trailingSignals.text(data.TrailingSignals.length);\n        trailingSignals.attr(\"title\", \"Signals:\\r\\n\" + data.TrailingSignals.join(\"\\r\\n\"));\n        var healthChecks = $(\"#healthChecks\");\n        if (data.TradingSuspended) {\n            healthChecks.removeClass(\"badge-success\");\n            healthChecks.addClass(\"badge-danger\");\n            healthChecks.text(\"OFF\");\n        }\n        else {\n            healthChecks.removeClass(\"badge-danger\");\n            healthChecks.addClass(\"badge-success\");\n            healthChecks.text(\"ON\");\n        }\n        data.HealthChecks.sort(function (a, b) { return a.Name > b.Name; });\n        healthChecks.attr(\"title\", data.HealthChecks.map(function (check) { return check.Name + \": \" + new Date(check.LastUpdated).toTimeString().split(' ')[0] + (check.Failed ? \" (Failed)\" : \" (OK)\"); }).join(\"\\r\\n\"));\n        var logEntries = $(\"#logEntries\");\n        logEntries.html(data.LogEntries.join(\"<br />\"));\n    }).fail(function (data) {\n        setStatus(\"error\");\n    });\n}\n\nfunction setStatus(status) {\n    if (status == \"refreshing\") {\n        $(\"#statusRefreshIcon\").stop().fadeIn(700).fadeOut(700);\n        $(\"#statusWarningIcon\").stop().hide();\n    }\n    else if (status == \"error\") {\n        $(\"#statusRefreshIcon\").stop().hide();\n        $(\"#statusWarningIcon\").stop().show();\n    }\n    else {\n        $(\"#statusRefreshIcon\").stop().hide();\n        $(\"#statusWarningIcon\").stop().hide();\n    }\n}\n\njQuery.fn.dataTable.Api.register('average()', function () {\n    var data = this.flatten();\n    var sum = data.reduce(function (a, b) {\n        return (a * 1) + (b * 1); // cast values in-case they are strings\n    }, 0);\n    return sum / data.length;\n});\n\njQuery.fn.dataTable.Api.register('sum()', function () {\n    var data = this.flatten();\n    var sum = data.reduce(function (a, b) {\n        return (a * 1) + (b * 1); // cast values in-case they are strings\n    }, 0);\n    return sum;\n});"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/Views/dashboard.css",
    "content": "﻿body {\n}\n\n#tradingPairsTable tr.even, #tradingPairsTable tr.odd {\n    cursor: pointer;\n}\n\n.row-details {\n    padding: 10px;\n}\n\n.row-details-name {\n    width: 115px;\n    display: inline-block;\n    font-weight: bold;\n}\n\n.row-details-value {\n\n}\n\n.additional-details {\n    float: left;\n    margin-bottom: 10px;\n}\n\n.show-settings {\n    float: right;\n    margin-left: 15px;\n}\n\n.manual-order {\n    float: right;\n}\n\n.manual-order input[type=text] {\n    width: 60px;\n    padding: 2px;\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/Views/help.css",
    "content": "﻿#md-page-menu a {\n    color: #5bc0de;\n}\n\n#md-content table a {\n    color: #DF691A;\n    text-decoration: none;\n}\n\n#md-content table tr:nth-child(odd) td {\n    padding: 8px;\n    background: #35495d;\n}\n\n#md-content table tr:nth-child(even) td {\n    padding: 8px;\n}\n\n#md-content table th {\n    border: 1px solid #5f7286;\n    background: #4d6a86;\n}\n\n#md-content table td {\n    border: 1px solid #5f7286;\n}\n\nh2.md-inpage-anchor {\n    padding-bottom: 15px;\n    border-bottom: 1px solid #4E5D6C;\n}\n\nh4.md-inpage-anchor {\n    padding-top: 5px;\n    padding-bottom: 5px;\n}\n\nh6.md-inpage-anchor {\n    padding-top: 5px;\n    color: #88e4ff;\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/Views/market.css",
    "content": "﻿body {\n\n}\n\n#marketPairsTable tr.even, #marketPairsTable tr.odd {\n    cursor: pointer;\n}\n\n.row-details {\n    padding: 10px;\n}\n\n.row-details-name {\n    width: 100px;\n    display: inline-block;\n    font-weight: bold;\n}\n\n.row-details-value {\n}\n\n.additional-details {\n    float: left;\n    margin-bottom: 10px;\n}\n\n.show-settings {\n    float: right;\n}\n\n.manual-order {\n    float: right;\n    margin-right: 15px;\n}\n\n.manual-order input[type=text] {\n    width: 60px;\n    padding: 2px;\n}\n\n.signals-filter {\n    float: right;\n    margin-left: 10px;\n}\n\n.signal-filter-option label {\n    color: white !important;\n}\n\n.signal-details {\n    width: 180px;\n}\n\n.signal-name {\n    display: inline-block;\n    width: 80px;\n}\n\n.signal-value {\n\n}\n\n.filter {\n    display: block;\n    margin-bottom: 10px;\n    width: 100px;\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/Views/rules.css",
    "content": "﻿body {\n}\n\n.sub-header {\n    clear: both;\n    margin: 15px;\n}\n"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/Views/settings.css",
    "content": "﻿body {\n}\n\n.settings {\n    float: left;\n}\n\n.settings div {\n    display: inline-block;\n    margin: 0 5px;\n}\n\n.settings label {\n    margin-left: 3px;\n}\n\n.settings-btn {\n    float: right;\n    margin-left: 10px;\n    padding: 5px;\n    font-size: 14px;\n}\n\n.configs {\n    clear: both;\n    padding-top: 20px;\n}\n\n.config-container {\n    background-color: white;\n}\n\n.config-value {\n    display: none;\n}\n\n.config-editor {\n    \n}\n\n.config-options {\n    text-align: right;\n    padding: 5px;\n}\n\n.save-config-status {\n    margin-right: 10px;\n}\n\n.tab-content {\n    margin-bottom: 20px;\n}\n\ndiv.jsoneditor {\n    border: 1px solid #4a4a4a;\n}\n\ndiv.jsoneditor-menu {\n    background-color: #4E5D6C;\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/Views/stats.css",
    "content": "﻿body {\n}\n\n.sub-header {\n    clear: both;\n    margin: 15px;\n}\n\n.trades-link {\n    font-size: 14px;\n}\n\n.rules-analyzer {\n    float: right;\n    margin-left: 10px;\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/Views/trades.css",
    "content": "﻿body {\n}\n\n.sub-header {\n    clear: both;\n    margin: 15px;\n}"
  },
  {
    "path": "IntelliTrader.Web/Static/Styles/intellitrader.css",
    "content": "﻿body {\n    padding-top: 100px;\n}\n\ninput[type=search], input[type=text], input[type=password], input[type=search]:focus, input[type=text]:focus, input[type=password]:focus {\n    background-color: #2e3740;\n    color: #ebebeb;\n    border: 1px solid #ced4da;\n    padding: 4px;\n}\n\n.page-header {\n    float: left;\n}\n\n.status-bar {\n    font-weight: bold;\n}\n\n.status-bar-btn {\n    border: none;\n    padding: 1px 6px;\n}\n\n.status-icons {\n    display: inline-block; \n    width:25px;\n}\n\n.status-icon {\n    margin-left: 10px;\n}\n\n.log-entries {\n    font-size: 12px;\n    float: left;\n    padding: 0 20px 20px 20px;\n}\n\ntable.dataTable {\n    border-collapse: collapse !important;\n}\n\ntable.dataTable td, table.dataTable th {\n    padding: 2px;\n}\n\ntable.dataTable td {\n    border-top: 1px solid #696969;\n}\n\ntable.dataTable th {\n    border: none;\n}\n\ntable.dataTable a.btn {\n    padding: 0 5px;\n    font-weight: bold;\n    color: white;\n}\n\n.dt-buttons {\n    float: right;\n    margin-left: 15px;\n    margin-bottom: 15px;\n}\n\n.dataTables_filter {\n    float: right;\n    margin-bottom: 15px;\n}\n\ndiv.dataTables_wrapper div.dataTables_filter input {\n    width: 140px;\n}\n\ntable.dataTable.dtr-column > tbody > tr > td.control:before, table.dataTable.dtr-column > tbody > tr > th.control:before {\n    line-height: 16px;\n}"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Dashboard.cshtml",
    "content": "﻿@model DashboardViewModel\n@{\n    ViewData[\"Title\"] = \"Dashboard\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n}\n\n@section AddToHead{\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Views/dashboard.css\")\">\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Views/dashboard.js\")\"></script>\n}\n\n<div>\n    <table class=\"table table-hover table-striped\" id=\"tradingPairsTable\" style=\"width: 100%\">\n        <thead>\n            <tr>\n                <th>More</th>\n                <th>Pair</th>\n                <th>Pair</th>\n                <th>DCA</th>\n                <th>Margin</th>\n                <th>Target</th>\n                <th>Rating</th>\n                <th>Rating Bought</th>\n                <th>Age</th>\n                <th>Amount</th>\n                <th>Cost</th>\n                <th>Cost Bought</th>\n                <th>Price</th>\n                <th>Price Bought</th>\n                <th>Spread</th>\n                <th>Signal Rule</th>\n                <th>Trading Rules</th>\n                <th>Order Dates</th>\n                <th>Order Ids</th>\n            </tr>\n        </thead>\n        <tfoot>\n            <tr>\n                <th></th>\n                <th>Total</th>\n                <th>Total</th>\n                <th></th>\n                <th>Avg</th>\n                <th></th>\n                <th>Avg</th>\n                <th>Avg</th>\n                <th>Avg</th>\n                <th></th>\n                <th>Total</th>\n                <th>Total</th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n            </tr>\n        </tfoot>\n    </table>\n\n    <div class=\"modal fade\" id=\"modal\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n        <div class=\"modal-dialog modal-dialog-centered\" role=\"document\">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                <h5 class=\"modal-title\" id=\"modalTitle\"></h5>\n                <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n                    <span aria-hidden=\"true\">&times;</span>\n                </button>\n                </div>\n                <div class=\"modal-body\" id=\"modalContent\"></div>\n                <div class=\"modal-footer\">\n                <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Close</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script type=\"text/html\" id=\"rowDetails\">\n        <div>\n            <div class=\"row-details\">\n                <input type=\"hidden\" id=\"pair\" />\n                <div class=\"additional-details\">\n                    <div id=\"swapPairContainer\">\n                        <span class=\"row-details-name\">Swap Pair:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"swapPair\"></span>\n                    </div>\n                    <div>\n                        <span class=\"row-details-name\">Signal Rule:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"signalRule\"></span>\n                    </div>\n                    <div>\n                        <span class=\"row-details-name\">Trading Rules:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"tradingRules\"></span>\n                    </div>\n                    <div>\n                        <span class=\"row-details-name\">Last Buy Margin:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"lastBuyMargin\"></span>\n                    </div>\n                    <div>\n                        <span class=\"row-details-name\">Order Dates:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"orderDates\"></span>\n                    </div>\n                    <div>\n                        <span class=\"row-details-name\">Order Ids:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"orderIds\"></span>\n                    </div>\n                </div>\n                <div class=\"show-settings\">\n                    <button class=\"btn btn-sm btn-success\" onclick=\"showSettings(this);\">Show Settings</button>\n                </div>\n                <div class=\"manual-order\">\n                    <span>Amount:</span>&nbsp;&nbsp;<input type=\"text\" id=\"amount\" />\n                    <button class=\"btn btn-sm btn-info\" onclick=\"sellPair(this);\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Sell</button>\n                    <button class=\"btn btn-sm btn-warning\" onclick=\"buyPair(this);\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Buy</button>\n                    <button class=\"btn btn-sm btn-primary\" onclick=\"swapPair(this);\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Swap</button>\n                </div>\n            </div>\n        </div>\n    </script>\n</div>\n"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Help.cshtml",
    "content": "﻿@model HelpViewModel\n@{\n    ViewData[\"Title\"] = \"Help\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n    ViewData[\"CustomPageHeader\"] = true;\n}\n\n@section AddToHead{\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/mdwiki.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/highlight.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/colorbox.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Views/help.css\")\">\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/highlight.min.js\")\"></script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/jquery.colorbox.min.js\")\"></script>\n    <script type=\"text/javascript\">\n        $.mdContentRoot = \"/Static/Help/\";\n    </script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/mdwiki.min.js\")\"></script>\n}\n\n<div id=\"md-all\" style=\"clear: both;\">\n</div>"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Log.cshtml",
    "content": "﻿@model LogViewModel\n@{\n    ViewData[\"Title\"] = \"Log\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n}\n\n<div style=\"clear: both; font-size: smaller\">\n    <hr />\n    <div class=\"log\">\n        @foreach (var entry in Model.LogEntries) {\n        <div>@Html.Raw(entry)</div>\n        }\n    </div>\n    <hr />\n</div>\n"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Login.cshtml",
    "content": "﻿@model LoginViewModel\n@{\n    ViewData[\"Title\"] = \"Login\";\n}\n\n<div style=\"clear: both; width: 300px;\">\n    <form method=\"post\">\n        <div asp-validation-summary=\"All\" class=\"text-danger\"></div>\n        <div class=\"form-group\">\n            <label for=\"Password\">Password </label>\n            @Html.PasswordFor(m => m.Password, new { @class = \"form-control\" })\n        </div>\n        <div class=\"form-check\">\n            <label class=\"form-check-label\">\n                @Html.CheckBoxFor(m => m.RememberMe, new { @class = \"form-check-input\" })\n                Remember Me\n             </label>\n        </div>\n        <br />\n        <button type=\"submit\" class=\"btn btn-primary\">Login</button>\n        @Html.AntiForgeryToken()\n    </form>\n</div>"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Market.cshtml",
    "content": "﻿@model MarketViewModel\n@{\n    ViewData[\"Title\"] = \"Market\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n}\n\n@section AddToHead{\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/bootstrap-multiselect.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Views/market.css\")\">\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/bootstrap-multiselect.min.js\")\"></script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Views/market.js\")\"></script>\n}\n\n<div>\n    <table class=\"table table-hover table-striped\" id=\"marketPairsTable\" style=\"width: 100%\">\n        <thead>\n            <tr>\n                <th>More</th>\n                <th>Name</th>\n                <th>Rating</th>\n                <th>% Rating Change</th>\n                <th>Price</th>\n                <th>% Price Change</th>\n                <th>Spread</th>\n                <th>Arbitrage</th>\n                <th>Volume</th>\n                <th>% Volume Change</th>\n                <th>Volatility</th>\n                <th>Trading Rules</th>\n                <th>Signal Rules</th>\n            </tr>\n        </thead>\n        <tfoot>\n            <tr>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n                <th></th>\n            </tr>\n        </tfoot>\n    </table>\n\n    <div class=\"modal fade\" id=\"modal\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n        <div class=\"modal-dialog modal-dialog-centered\" role=\"document\">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                <h5 class=\"modal-title\" id=\"modalTitle\"></h5>\n                <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n                    <span aria-hidden=\"true\">&times;</span>\n                </button>\n                </div>\n                <div class=\"modal-body\" id=\"modalContent\"></div>\n                <div class=\"modal-footer\">\n                <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Close</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script type=\"text/html\" id=\"rowDetails\">\n        <div>\n            <div class=\"row-details\">\n                <input type=\"hidden\" id=\"pair\" />\n                <div class=\"additional-details\">\n                    <div>\n                        <span class=\"row-details-name\">Signal Rules:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"signalRules\"></span>\n                    </div>\n                    <div>\n                        <span class=\"row-details-name\">Trading Rules:</span>&nbsp;&nbsp;<span class=\"row-details-value\" id=\"tradingRules\"></span>\n                    </div>\n                </div>\n                <div class=\"show-settings\">\n                    <button class=\"btn btn-sm btn-success\" onclick=\"showSettings(this);\">Show Settings</button>\n                </div>\n                <div class=\"manual-order\">\n                    <span>Amount:</span>&nbsp;&nbsp;<input type=\"text\" id=\"amount\" value=\"0\" />\n                    <button class=\"btn btn-sm btn-warning\" onclick=\"buyPair(this);\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Buy</button>\n                    <button class=\"btn btn-sm btn-warning\" onclick=\"buyPairDefault(this);\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Buy Default</button>\n                </div>\n            </div>\n        </div>\n    </script>\n</div>\n"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Rules.cshtml",
    "content": "﻿@model RulesViewModel\n@{\n    ViewData[\"Title\"] = \"Rules Analyzer\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n}\n\n@section AddToHead{\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Views/rules.css\")\">\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Views/rules.js\")\"></script>\n}\n\n<div>\n    <div class=\"sub-header\">Signal Rules: @Model.SignalRuleStats.Count</div>\n    <table class=\"table table-hover table-striped\" id=\"rulesTable\" style=\"width: 100%\">\n        <thead>\n            <tr>\n                <th>Signal Rule</th>\n                <th>Profit</th>\n                <th>Fees</th>\n                <th>Cost</th>\n                <th>Trades</th>\n                <th>Orders</th>\n                <th>Swaps</th>\n                <th>Avg. Age</th>\n                <th>Avg. Margin</th>\n                <th>Avg. Margin DCA</th>\n                <th>Avg. DCA</th>\n            </tr>\n        </thead>\n        <tbody>\n            @foreach (var trade in Model.SignalRuleStats)\n            {\n            <tr>\n                <td>@trade.Key</td>\n                <td>@trade.Value.TotalProfit.ToString(\"0.00000000\")</td>\n                <td>@trade.Value.TotalFees.ToString(\"0.00000000\")</td>\n                <td>@trade.Value.TotalCost.ToString(\"0.00000000\")</td>\n                <td>@trade.Value.TotalTrades</td>\n                <td>@trade.Value.TotalOrders</td>\n                <td>@trade.Value.TotalSwaps</td>\n                <td>@trade.Value.Age.DefaultIfEmpty(0).Average().ToString(\"0.00\")</td>\n                <td>@trade.Value.Margin.DefaultIfEmpty(0).Average().ToString(\"0.00\")</td>\n                <td>@trade.Value.MarginDCA.DefaultIfEmpty(0).Average().ToString(\"0.00\")</td>\n                <td>@trade.Value.DCA.DefaultIfEmpty(0).Average().ToString(\"0.00\")</td>\n            </tr>\n            }\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Settings.cshtml",
    "content": "﻿@model SettingsViewModel\n@{\n    ViewData[\"Title\"] = \"Settings\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n    ViewData[\"CustomPageHeader\"] = true;\n}\n\n@section AddToHead{\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/jsoneditor.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Views/settings.css\")\">\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/jsoneditor.min.js\")\"></script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Views/settings.js\")\"></script>\n}\n\n<div style=\"clear: both\">\n    @using (Html.BeginForm())\n    {\n    <div class=\"settings\">\n        <div>@Html.CheckBoxFor(model => model.BuyEnabled)@Html.LabelFor(model => model.BuyEnabled)</div>\n        <div>@Html.CheckBoxFor(model => model.BuyDCAEnabled)@Html.LabelFor(model => model.BuyDCAEnabled)</div>\n        <div>@Html.CheckBoxFor(model => model.SellEnabled)@Html.LabelFor(model => model.SellEnabled)</div>\n        <div>@Html.CheckBoxFor(model => model.HealthCheckEnabled)@Html.LabelFor(model => model.HealthCheckEnabled)</div>\n        <div>@Html.CheckBoxFor(model => model.TradingSuspended)@Html.LabelFor(model => model.TradingSuspended)</div>\n        <button type=\"submit\" class=\"btn btn-primary settings-btn\" onclick=\"return confirm('Save settings?');\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Save</button>\n    </div>\n    }\n    <button class=\"btn btn-info settings-btn\" onclick=\"logout();\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Log Out</button>\n    <button class=\"btn btn-info settings-btn\" onclick=\"refreshAccount();\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Refresh Account</button>\n    <button class=\"btn btn-info settings-btn\" onclick=\"restartServices();\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Restart Services</button>\n\n    <div class=\"configs\">\n        <h2>Advanced</h2>\n\n        <ul class=\"nav nav-pills bg-dark\">\n            @foreach (var configName in Model.Configs.Keys)\n            {\n            <li class=\"nav-item\">\n                <a class=\"nav-link\" data-toggle=\"pill\" href=\"#tab_@configName\" role=\"tab\">@configName</a>\n            </li>\n            }\n        </ul>\n\n        <div class=\"tab-content\">\n        @foreach (var kvp in Model.Configs)\n        {\n            <div class=\"tab-pane\" id=\"tab_@kvp.Key\">\n                <div class=\"config-container\">\n                    <div class=\"config-value\" id=\"@Html.Raw(kvp.Key + \"Value\")\" data-config=\"@kvp.Key\">@Html.Raw(kvp.Value)</div>\n                    <div class=\"config-editor\" id=\"@Html.Raw(kvp.Key + \"Editor\")\" data-config=\"@kvp.Key\"></div>\n                </div>\n                <div class=\"config-options bg-dark\">\n                    <span class=\"save-config-status\" id=\"saveConfigStatus\"></span>\n                    <button class=\"btn btn-primary\" onclick=\"saveConfig(this);\" @if (Model.ReadOnlyMode) { <text>disabled</text> }>Save</button>\n                </div>\n            </div>\n        }\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Stats.cshtml",
    "content": "﻿@model StatsViewModel\n@{\n    ViewData[\"Title\"] = \"Stats\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n}\n\n@section AddToHead{\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Views/stats.css\")\">\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Views/stats.js\")\"></script>\n}\n\n<div>\n    <div class=\"sub-header\">Total Profit: @Model.Trades.Values.Sum(trades => trades.Where(t => !t.IsSwap).Sum(t => t.Profit)).ToString(\"0.00000000\") (@Model.Trades.Values.Sum(trades => trades.Where(t => !t.IsSwap).Sum(t => t.Profit) / Model.AccountInitialBalance * 100).ToString(\"0.00\")%) , Account Balance: @Model.AccountBalance.ToString(\"0.00000000\") @Model.Market</div>\n\n    <table class=\"table table-hover table-striped\" id=\"statsTable\" style=\"width: 100%\">\n        <thead>\n            <tr>\n                <th>Date</th>\n                <th>Trades</th>\n                <th>% Profit</th>\n                <th>Profit</th>\n                <th>Fees</th>\n                <th>Avg. Margin</th>\n                <th>Avg. Margin DCA</th>\n                <th>Avg. Rating Bought</th>\n                <th>Avg. Rating Sold</th>\n                <th>Avg. Gl. Rating Bought</th>\n                <th>Avg. Gl. Rating Sold</th>\n            </tr>\n        </thead>\n        <tbody>\n            @foreach (var kvp in Model.Trades.OrderByDescending(t => t.Key).Take(30))\n            {\n                <tr>\n                    <td>@kvp.Key.ToString(\"yyyy-MM-dd\")</td>\n                    <td>@Html.ActionLink(kvp.Value.Count.ToString(), \"Trades\", \"Home\", new { id = kvp.Key.ToString(\"o\") }, new { @class = \"btn btb-sm btn-success trades-link\" })</td>\n                    <td>@{ var percentage = kvp.Value.Where(t => !t.IsSwap).Sum(t => t.Profit) / Model.Balances[kvp.Key] * 100; @Html.Raw(percentage.ToString(\"0.00\")); }</td>\n                    <td>@kvp.Value.Where(t => !t.IsSwap).Sum(t => t.Profit).ToString(\"0.00000000\")</td>\n                    <td>@kvp.Value.Sum(t => t.FeesTotal).ToString(\"0.00000000\")</td>\n                    <td>\n                        @{ var trades = kvp.Value.Where(t => !t.IsSwap && t.OrderDates != null && t.OrderDates.Count == 1); if (trades.Count() > 0)\n                            {\n                                var avgMargin = trades.Average(t => t.Profit / (t.Cost + (t.Metadata?.AdditionalCosts ?? 0)) * 100);\n                                @Html.Raw(avgMargin.ToString(\"0.00\"));\n                            } else\n                            {\n                                @Html.Raw(\"N/A\");\n                            }\n                        }\n                    </td>\n                    <td>\n                        @{ var tradesDCA = kvp.Value.Where(t => !t.IsSwap && t.OrderDates != null && t.OrderDates.Count > 1); if (tradesDCA.Count() > 0)\n                            {\n                                var avgMargin = tradesDCA.Average(t => t.Profit / (t.Cost + (t.Metadata?.AdditionalCosts ?? 0)) * 100);\n                                @Html.Raw(avgMargin.ToString(\"0.00\"));\n                            } else\n                            {\n                                @Html.Raw(\"N/A\");\n                            }\n                        }\n                    </td>\n                    <td>\n                        @{ var tradesRatingBought = kvp.Value.Where(t => t.Metadata?.BoughtRating != null); if (tradesRatingBought.Count() > 0)\n                            {\n                                var avgRatingBought = tradesRatingBought.Average(t => t.Metadata.BoughtRating.Value);\n                                @Html.Raw(avgRatingBought.ToString(\"0.000\"));\n                            } else\n                            {\n                                @Html.Raw(\"N/A\");\n                            }\n                        }\n                    </td>\n                    <td>\n                        @{ var tradesRatingSold = kvp.Value.Where(t => t.Metadata?.CurrentRating != null); if (tradesRatingSold.Count() > 0)\n                            {\n                                var avgRatingSold = tradesRatingSold.Average(t => t.Metadata.CurrentRating.Value);\n                                @Html.Raw(avgRatingSold.ToString(\"0.000\"));\n                            } else\n                            {\n                                @Html.Raw(\"N/A\");\n                            }\n                        }\n                    </td>\n                    <td>\n                        @{ var tradesGlobalRatingBought = kvp.Value.Where(t => t.Metadata?.BoughtGlobalRating != null); if (tradesGlobalRatingBought.Count() > 0)\n                            {\n                                var avgGlobalRatingBought = tradesGlobalRatingBought.Average(t => t.Metadata.BoughtGlobalRating.Value);\n                                @Html.Raw(avgGlobalRatingBought.ToString(\"0.000\"));\n                            } else\n                            {\n                                @Html.Raw(\"N/A\");\n                            }\n                        }\n                    </td>\n                    <td>\n                        @{ var tradesGlobalRatingSold = kvp.Value.Where(t => t.Metadata?.CurrentGlobalRating != null); if (tradesGlobalRatingSold.Count() > 0)\n                            {\n                                var avgGlobalRatingSold = tradesGlobalRatingSold.Average(t => t.Metadata.CurrentGlobalRating.Value);\n                                @Html.Raw(avgGlobalRatingSold.ToString(\"0.000\"));\n                            } else\n                            {\n                                @Html.Raw(\"N/A\");\n                            }\n                        }\n                    </td>\n                </tr>\n    }\n        </tbody>\n    </table>\n    <hr />\n</div>\n"
  },
  {
    "path": "IntelliTrader.Web/Views/Home/Trades.cshtml",
    "content": "﻿@model TradesViewModel\n@{\n    ViewData[\"Title\"] = \"Trades\";\n    ViewData[\"Instance\"] = Model.InstanceName;\n}\n\n@section AddToHead{\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Views/trades.css\")\">\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Views/trades.js\")\"></script>\n}\n\n<div>\n    <div class=\"sub-header\">Date: @Model.Date.ToOffset(TimeSpan.FromHours(Model.TimezoneOffset)).ToString(\"yyyy-MM-dd\"), Total: @Model.Trades.Count</div>\n    <table class=\"table table-hover table-striped\" id=\"tradesTable\" style=\"width: 100%\">\n        <thead>\n            <tr>\n                <th>Date</th>\n                <th>Pair</th>\n                <th>DCA Level</th>\n                <th>Margin</th>\n                <th>Profit</th>\n                <th>Fees</th>\n                <th>Age</th>\n                <th>Rating Bought</th>\n                <th>Rating Sold</th>\n                <th>Gl. Rating Bought</th>\n                <th>Gl. Rating Sold</th>\n                <th>Signal Rule</th>\n                <th>Swap Pair</th>\n                <th>Arbitrage</th>\n                <th>% Arbitrage</th>\n            </tr>\n        </thead>\n        <tbody>\n            @foreach (var trade in Model.Trades.OrderByDescending(t => t.SellDate))\n            {\n            <tr>\n                <td>@trade.SellDate.ToOffset(TimeSpan.FromHours(Model.TimezoneOffset)).ToString(\"yyyy-MM-dd HH:mm:ss\")</td>\n                <td>@if (trade.Metadata?.OriginalPair != null) { @Html.Raw(trade.Metadata.OriginalPair); } else { @Html.Raw(trade.Pair); }</td>\n                <td>@if (trade.Metadata?.OriginalPair == null) { @Html.Raw((trade.OrderDates.Count - 1) + (trade.Metadata?.AdditionalDCALevels ?? 0)) } else { <text>0</text> }</td>\n                <td>@Html.Raw((trade.Profit / (trade.Cost + (trade.Metadata?.AdditionalCosts ?? 0)) * 100).ToString(\"0.00\"))</td>\n                <td>@trade.Profit.ToString(\"0.00000000\")</td>\n                <td>@trade.FeesTotal.ToString(\"0.00000000\")</td>\n                <td>@if (trade.OrderDates != null && trade.OrderDates.Count > 0) { @Html.Raw((trade.SellDate - trade.OrderDates.Min()).TotalDays.ToString(\"0.00\")); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.BoughtRating != null) { @Html.Raw(trade.Metadata.BoughtRating.Value.ToString(\"0.000\")); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.CurrentRating != null) { @Html.Raw(trade.Metadata.CurrentRating.Value.ToString(\"0.000\")); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.BoughtGlobalRating != null) { @Html.Raw(trade.Metadata.BoughtGlobalRating.Value.ToString(\"0.000\")); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.CurrentGlobalRating != null) { @Html.Raw(trade.Metadata.CurrentGlobalRating.Value.ToString(\"0.000\")); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.SignalRule != null) { @Html.Raw(trade.Metadata.SignalRule); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.SwapPair != null && trade.IsSwap) { @Html.Raw(trade.Metadata.SwapPair); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.Arbitrage != null && trade.IsArbitrage) { @Html.Raw(trade.Metadata.Arbitrage); } else { <text>N/A</text> }</td>\n                <td>@if (trade.Metadata?.ArbitragePercentage != null && trade.IsArbitrage) { @Html.Raw(trade.Metadata.ArbitragePercentage.GetValueOrDefault().ToString(\"0.00\")); } else { <text>N/A</text> }</td>\n            </tr>\n            }\n        </tbody>\n    </table>\n</div>\n"
  },
  {
    "path": "IntelliTrader.Web/Views/Shared/_Layout.cshtml",
    "content": "﻿<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    @if (User.Identity.IsAuthenticated)\n    {\n        <title>@ViewData[\"Title\"] - IntelliTrader (@ViewData[\"Instance\"])</title>\n    }\n    else\n    {\n        <title>@ViewData[\"Title\"] - IntelliTrader</title>\n    }\n\n    <link rel=\"icon\" href=\"@Url.Content(\"~/Static/Images/favicon.ico\")\" type=\"image/x-icon\" />\n    <link rel=\"shortcut icon\" href=\"@Url.Content(\"~/Static/Images/favicon.ico\")\" type=\"image/x-icon\" />\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/bootstrap.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/bootstrap-theme.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/fontawesome-all.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/Vendor/datatables.min.css\")\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"@Url.Content(\"~/Static/Styles/intellitrader.css\")\">\n\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/jquery-3.3.1.min.js\")\"></script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/popper.min.js\")\"></script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/bootstrap.min.js\")\"></script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/Vendor/datatables.min.js\")\"></script>\n    <script type=\"text/javascript\" src=\"@Url.Content(\"~/Static/Scripts/intellitrader.js\")\"></script>\n\n    @if (IsSectionDefined(\"AddToHead\"))\n    {\n        @RenderSection(\"AddToHead\", required: false)\n    }\n\n    <script>\n        var isAuthenticated = @User.Identity.IsAuthenticated.ToString().ToLowerInvariant();\n    </script>\n</head>\n<body>\n    <nav class=\"navbar navbar-expand-lg fixed-top navbar-dark bg-dark\">\n        @if (!User.Identity.IsAuthenticated)\n        {\n            <a class=\"navbar-brand\" href=\"/\"><img src=\"~/Static/Images/logo.png\" width=\"30\" height=\"30\" class=\"d-inline-block align-top\" alt=\"Logo\">IntelliTrader</a>\n\n        } else {\n            <a class=\"navbar-brand\" href=\"/\"><img src=\"~/Static/Images/logo.png\" width=\"30\" height=\"30\" class=\"d-inline-block align-top\" alt=\"Logo\">IntelliTrader (@ViewData[\"Instance\"])</a>\n            <button class=\"navbar-toggler\" type=\"button\" data-toggle=\"collapse\" data-target=\"#navbarNavAltMarkup\" aria-controls=\"navbarNavAltMarkup\" aria-expanded=\"false\" aria-label=\"Toggle navigation\">\n                <span class=\"navbar-toggler-icon\"></span>\n            </button>\n            <div class=\"collapse navbar-collapse\" id=\"navbarNavAltMarkup\">\n                <div class=\"navbar-nav\">\n                    <a class=\"nav-item nav-link\" href=\"/\">Dashboard</a>\n                    <a class=\"nav-item nav-link\" href=\"/Market\">Market</a>\n                    <a class=\"nav-item nav-link\" href=\"/Stats\">Stats</a>\n                    <a class=\"nav-item nav-link\" href=\"/Settings\">Settings</a>\n                    <a class=\"nav-item nav-link\" href=\"/Log\">Log</a>\n                    <a class=\"nav-item nav-link\" href=\"/Help\">Help</a>\n                    <span class=\"nav-item nav-link text-muted\" title=\"Lovingly handcrafted by Jazzo\"><text>v</text>@Model.Version</span>\n                </div>\n            </div>\n            <div class=\"navbar-text status-bar\">\n                <span title=\"Available Balance\">B:</span> <span class=\"text-success\" id=\"accountBalance\" title=\"Available Balance\">0</span> |\n                <span title=\"Global Rating\">R:</span> <span id=\"globalRating\" title=\"Global Rating\">N/A</span> |\n                <span title=\"Trailing\">T:</span> <span class=\"text-info\" id=\"trailingBuys\">0</span>/<span class=\"text-info\" id=\"trailingSells\">0</span>/<span class=\"text-info\" id=\"trailingSignals\">0</span> |\n                <span title=\"Status\">S:</span> <span class=\"badge badge-pill badge-success\" id=\"healthChecks\">ON</span>\n                <div class=\"status-icons\">\n                    <i class=\"fas fa-spinner text-info status-icon\" id=\"statusRefreshIcon\" title=\"Getting Status Info...\"></i>\n                    <i class=\"fas fa-exclamation-triangle text-warning status-icon\" id=\"statusWarningIcon\" title=\"Connection Error\" style=\"display: none\"></i>\n                </div>\n            </div>\n        }\n    </nav>\n    <div>\n        <div class=\"container-fluid\">\n            @if (!ViewData.ContainsKey(\"CustomPageHeader\")) { <h3 class=\"page-header\">@ViewData[\"Title\"]</h3> }\n            <div class=\"collapse log-entries\" id=\"logEntries\"></div>\n            @RenderBody()\n        </div>\n    </div>\n</body>\n</html>"
  },
  {
    "path": "IntelliTrader.Web/Views/_ViewImports.cshtml",
    "content": "@using IntelliTrader.Web\n@using IntelliTrader.Web.Models\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n"
  },
  {
    "path": "IntelliTrader.Web/Views/_ViewStart.cshtml",
    "content": "﻿@{\n    Layout = \"_Layout\";\n}\n"
  },
  {
    "path": "IntelliTrader.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.27130.2027\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader\", \"IntelliTrader\\IntelliTrader.csproj\", \"{E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Core\", \"IntelliTrader.Core\\IntelliTrader.Core.csproj\", \"{E4702F59-BE3D-41E9-9769-B49122AE37F3}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Signals\", \"Signals\", \"{E9A5196B-B397-4AC8-90F1-7DCFB887E4DF}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Signals.Base\", \"IntelliTrader.Signals.Base\\IntelliTrader.Signals.Base.csproj\", \"{60F0F4CC-9480-4C6E-B753-6754E47A4EE3}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Signals.TradingView\", \"IntelliTrader.Signals.TradingView\\IntelliTrader.Signals.TradingView.csproj\", \"{7587243F-1361-4749-85EA-457303C48DC5}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Exchange\", \"Exchange\", \"{7C3B0DA6-D479-4B65-994E-4277DF073B83}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Exchange.Base\", \"IntelliTrader.Exchange.Base\\IntelliTrader.Exchange.Base.csproj\", \"{55A29746-4B68-42B6-BC07-5243016BDA17}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Exchange.Binance\", \"IntelliTrader.Exchange.Binance\\IntelliTrader.Exchange.Binance.csproj\", \"{A7487CA9-F3F6-496E-9D58-11D893BD8813}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Trading\", \"IntelliTrader.Trading\\IntelliTrader.Trading.csproj\", \"{F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Submodules\", \"Submodules\", \"{3509C1FF-281E-4334-8D84-D161488A3DC6}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ExchangeSharp\", \"Submodules\\ExchangeSharp\\ExchangeSharp\\ExchangeSharp.csproj\", \"{6569167D-5CD5-488D-9492-43210C0AEC0D}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Web\", \"IntelliTrader.Web\\IntelliTrader.Web.csproj\", \"{802FF51C-E66D-44BC-B26F-C72C68ED9E56}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Rules\", \"IntelliTrader.Rules\\IntelliTrader.Rules.csproj\", \"{A7177BC2-2912-440A-BBBE-A2DE3D001918}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IntelliTrader.Backtesting\", \"IntelliTrader.Backtesting\\IntelliTrader.Backtesting.csproj\", \"{20657014-0DC0-43E6-8351-E94F78095DFC}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{33CCA636-C1F7-419E-AF89-B771E5CEC0BD}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.gitignore = .gitignore\n\t\t.gitmodules = .gitmodules\n\t\tDisclaimer.txt = Disclaimer.txt\n\t\tLicense.txt = License.txt\n\t\tPublish.bat = Publish.bat\n\t\tPublish.sh = Publish.sh\n\t\tREADME.md = README.md\n\tEndProjectSection\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E4702F59-BE3D-41E9-9769-B49122AE37F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E4702F59-BE3D-41E9-9769-B49122AE37F3}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E4702F59-BE3D-41E9-9769-B49122AE37F3}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E4702F59-BE3D-41E9-9769-B49122AE37F3}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{7587243F-1361-4749-85EA-457303C48DC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{7587243F-1361-4749-85EA-457303C48DC5}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{7587243F-1361-4749-85EA-457303C48DC5}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{7587243F-1361-4749-85EA-457303C48DC5}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{55A29746-4B68-42B6-BC07-5243016BDA17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{55A29746-4B68-42B6-BC07-5243016BDA17}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{55A29746-4B68-42B6-BC07-5243016BDA17}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{55A29746-4B68-42B6-BC07-5243016BDA17}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{A7487CA9-F3F6-496E-9D58-11D893BD8813}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{A7487CA9-F3F6-496E-9D58-11D893BD8813}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{A7487CA9-F3F6-496E-9D58-11D893BD8813}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{A7487CA9-F3F6-496E-9D58-11D893BD8813}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6569167D-5CD5-488D-9492-43210C0AEC0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6569167D-5CD5-488D-9492-43210C0AEC0D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6569167D-5CD5-488D-9492-43210C0AEC0D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6569167D-5CD5-488D-9492-43210C0AEC0D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{A7177BC2-2912-440A-BBBE-A2DE3D001918}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{A7177BC2-2912-440A-BBBE-A2DE3D001918}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{A7177BC2-2912-440A-BBBE-A2DE3D001918}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{A7177BC2-2912-440A-BBBE-A2DE3D001918}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{20657014-0DC0-43E6-8351-E94F78095DFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{20657014-0DC0-43E6-8351-E94F78095DFC}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{20657014-0DC0-43E6-8351-E94F78095DFC}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{20657014-0DC0-43E6-8351-E94F78095DFC}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{60F0F4CC-9480-4C6E-B753-6754E47A4EE3} = {E9A5196B-B397-4AC8-90F1-7DCFB887E4DF}\n\t\t{7587243F-1361-4749-85EA-457303C48DC5} = {E9A5196B-B397-4AC8-90F1-7DCFB887E4DF}\n\t\t{55A29746-4B68-42B6-BC07-5243016BDA17} = {7C3B0DA6-D479-4B65-994E-4277DF073B83}\n\t\t{A7487CA9-F3F6-496E-9D58-11D893BD8813} = {7C3B0DA6-D479-4B65-994E-4277DF073B83}\n\t\t{6569167D-5CD5-488D-9492-43210C0AEC0D} = {3509C1FF-281E-4334-8D84-D161488A3DC6}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {524E84EB-C228-46B9-B474-621BC86BB3E5}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "License.txt",
    "content": "Attribution-NonCommercial-ShareAlike 4.0 International\n\n=======================================================================\n\nCreative Commons Corporation (\"Creative Commons\") is not a law firm and\ndoes not provide legal services or legal advice. Distribution of\nCreative Commons public licenses does not create a lawyer-client or\nother relationship. Creative Commons makes its licenses and related\ninformation available on an \"as-is\" basis. Creative Commons gives no\nwarranties regarding its licenses, any material licensed under their\nterms and conditions, or any related information. Creative Commons\ndisclaims all liability for damages resulting from their use to the\nfullest extent possible.\n\nUsing Creative Commons Public Licenses\n\nCreative Commons public licenses provide a standard set of terms and\nconditions that creators and other rights holders may use to share\noriginal works of authorship and other material subject to copyright\nand certain other rights specified in the public license below. The\nfollowing considerations are for informational purposes only, are not\nexhaustive, and do not form part of our licenses.\n\n     Considerations for licensors: Our public licenses are\n     intended for use by those authorized to give the public\n     permission to use material in ways otherwise restricted by\n     copyright and certain other rights. Our licenses are\n     irrevocable. Licensors should read and understand the terms\n     and conditions of the license they choose before applying it.\n     Licensors should also secure all rights necessary before\n     applying our licenses so that the public can reuse the\n     material as expected. Licensors should clearly mark any\n     material not subject to the license. This includes other CC-\n     licensed material, or material used under an exception or\n     limitation to copyright. More considerations for licensors:\n\twiki.creativecommons.org/Considerations_for_licensors\n\n     Considerations for the public: By using one of our public\n     licenses, a licensor grants the public permission to use the\n     licensed material under specified terms and conditions. If\n     the licensor's permission is not necessary for any reason--for\n     example, because of any applicable exception or limitation to\n     copyright--then that use is not regulated by the license. Our\n     licenses grant only permissions under copyright and certain\n     other rights that a licensor has authority to grant. Use of\n     the licensed material may still be restricted for other\n     reasons, including because others have copyright or other\n     rights in the material. A licensor may make special requests,\n     such as asking that all changes be marked or described.\n     Although not required by our licenses, you are encouraged to\n     respect those requests where reasonable. More considerations\n     for the public: \n\twiki.creativecommons.org/Considerations_for_licensees\n\n=======================================================================\n\nCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International\nPublic License\n\nBy exercising the Licensed Rights (defined below), You accept and agree\nto be bound by the terms and conditions of this Creative Commons\nAttribution-NonCommercial-ShareAlike 4.0 International Public License\n(\"Public License\"). To the extent this Public License may be\ninterpreted as a contract, You are granted the Licensed Rights in\nconsideration of Your acceptance of these terms and conditions, and the\nLicensor grants You such rights in consideration of benefits the\nLicensor receives from making the Licensed Material available under\nthese terms and conditions.\n\n\nSection 1 -- Definitions.\n\n  a. Adapted Material means material subject to Copyright and Similar\n     Rights that is derived from or based upon the Licensed Material\n     and in which the Licensed Material is translated, altered,\n     arranged, transformed, or otherwise modified in a manner requiring\n     permission under the Copyright and Similar Rights held by the\n     Licensor. For purposes of this Public License, where the Licensed\n     Material is a musical work, performance, or sound recording,\n     Adapted Material is always produced where the Licensed Material is\n     synched in timed relation with a moving image.\n\n  b. Adapter's License means the license You apply to Your Copyright\n     and Similar Rights in Your contributions to Adapted Material in\n     accordance with the terms and conditions of this Public License.\n\n  c. BY-NC-SA Compatible License means a license listed at\n     creativecommons.org/compatiblelicenses, approved by Creative\n     Commons as essentially the equivalent of this Public License.\n\n  d. Copyright and Similar Rights means copyright and/or similar rights\n     closely related to copyright including, without limitation,\n     performance, broadcast, sound recording, and Sui Generis Database\n     Rights, without regard to how the rights are labeled or\n     categorized. For purposes of this Public License, the rights\n     specified in Section 2(b)(1)-(2) are not Copyright and Similar\n     Rights.\n\n  e. Effective Technological Measures means those measures that, in the\n     absence of proper authority, may not be circumvented under laws\n     fulfilling obligations under Article 11 of the WIPO Copyright\n     Treaty adopted on December 20, 1996, and/or similar international\n     agreements.\n\n  f. Exceptions and Limitations means fair use, fair dealing, and/or\n     any other exception or limitation to Copyright and Similar Rights\n     that applies to Your use of the Licensed Material.\n\n  g. License Elements means the license attributes listed in the name\n     of a Creative Commons Public License. The License Elements of this\n     Public License are Attribution, NonCommercial, and ShareAlike.\n\n  h. Licensed Material means the artistic or literary work, database,\n     or other material to which the Licensor applied this Public\n     License.\n\n  i. Licensed Rights means the rights granted to You subject to the\n     terms and conditions of this Public License, which are limited to\n     all Copyright and Similar Rights that apply to Your use of the\n     Licensed Material and that the Licensor has authority to license.\n\n  j. Licensor means the individual(s) or entity(ies) granting rights\n     under this Public License.\n\n  k. NonCommercial means not primarily intended for or directed towards\n     commercial advantage or monetary compensation. For purposes of\n     this Public License, the exchange of the Licensed Material for\n     other material subject to Copyright and Similar Rights by digital\n     file-sharing or similar means is NonCommercial provided there is\n     no payment of monetary compensation in connection with the\n     exchange.\n\n  l. Share means to provide material to the public by any means or\n     process that requires permission under the Licensed Rights, such\n     as reproduction, public display, public performance, distribution,\n     dissemination, communication, or importation, and to make material\n     available to the public including in ways that members of the\n     public may access the material from a place and at a time\n     individually chosen by them.\n\n  m. Sui Generis Database Rights means rights other than copyright\n     resulting from Directive 96/9/EC of the European Parliament and of\n     the Council of 11 March 1996 on the legal protection of databases,\n     as amended and/or succeeded, as well as other essentially\n     equivalent rights anywhere in the world.\n\n  n. You means the individual or entity exercising the Licensed Rights\n     under this Public License. Your has a corresponding meaning.\n\n\nSection 2 -- Scope.\n\n  a. License grant.\n\n       1. Subject to the terms and conditions of this Public License,\n          the Licensor hereby grants You a worldwide, royalty-free,\n          non-sublicensable, non-exclusive, irrevocable license to\n          exercise the Licensed Rights in the Licensed Material to:\n\n            a. reproduce and Share the Licensed Material, in whole or\n               in part, for NonCommercial purposes only; and\n\n            b. produce, reproduce, and Share Adapted Material for\n               NonCommercial purposes only.\n\n       2. Exceptions and Limitations. For the avoidance of doubt, where\n          Exceptions and Limitations apply to Your use, this Public\n          License does not apply, and You do not need to comply with\n          its terms and conditions.\n\n       3. Term. The term of this Public License is specified in Section\n          6(a).\n\n       4. Media and formats; technical modifications allowed. The\n          Licensor authorizes You to exercise the Licensed Rights in\n          all media and formats whether now known or hereafter created,\n          and to make technical modifications necessary to do so. The\n          Licensor waives and/or agrees not to assert any right or\n          authority to forbid You from making technical modifications\n          necessary to exercise the Licensed Rights, including\n          technical modifications necessary to circumvent Effective\n          Technological Measures. For purposes of this Public License,\n          simply making modifications authorized by this Section 2(a)\n          (4) never produces Adapted Material.\n\n       5. Downstream recipients.\n\n            a. Offer from the Licensor -- Licensed Material. Every\n               recipient of the Licensed Material automatically\n               receives an offer from the Licensor to exercise the\n               Licensed Rights under the terms and conditions of this\n               Public License.\n\n            b. Additional offer from the Licensor -- Adapted Material.\n               Every recipient of Adapted Material from You\n               automatically receives an offer from the Licensor to\n               exercise the Licensed Rights in the Adapted Material\n               under the conditions of the Adapter's License You apply.\n\n            c. No downstream restrictions. You may not offer or impose\n               any additional or different terms or conditions on, or\n               apply any Effective Technological Measures to, the\n               Licensed Material if doing so restricts exercise of the\n               Licensed Rights by any recipient of the Licensed\n               Material.\n\n       6. No endorsement. Nothing in this Public License constitutes or\n          may be construed as permission to assert or imply that You\n          are, or that Your use of the Licensed Material is, connected\n          with, or sponsored, endorsed, or granted official status by,\n          the Licensor or others designated to receive attribution as\n          provided in Section 3(a)(1)(A)(i).\n\n  b. Other rights.\n\n       1. Moral rights, such as the right of integrity, are not\n          licensed under this Public License, nor are publicity,\n          privacy, and/or other similar personality rights; however, to\n          the extent possible, the Licensor waives and/or agrees not to\n          assert any such rights held by the Licensor to the limited\n          extent necessary to allow You to exercise the Licensed\n          Rights, but not otherwise.\n\n       2. Patent and trademark rights are not licensed under this\n          Public License.\n\n       3. To the extent possible, the Licensor waives any right to\n          collect royalties from You for the exercise of the Licensed\n          Rights, whether directly or through a collecting society\n          under any voluntary or waivable statutory or compulsory\n          licensing scheme. In all other cases the Licensor expressly\n          reserves any right to collect such royalties, including when\n          the Licensed Material is used other than for NonCommercial\n          purposes.\n\n\nSection 3 -- License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the\nfollowing conditions.\n\n  a. Attribution.\n\n       1. If You Share the Licensed Material (including in modified\n          form), You must:\n\n            a. retain the following if it is supplied by the Licensor\n               with the Licensed Material:\n\n                 i. identification of the creator(s) of the Licensed\n                    Material and any others designated to receive\n                    attribution, in any reasonable manner requested by\n                    the Licensor (including by pseudonym if\n                    designated);\n\n                ii. a copyright notice;\n\n               iii. a notice that refers to this Public License;\n\n                iv. a notice that refers to the disclaimer of\n                    warranties;\n\n                 v. a URI or hyperlink to the Licensed Material to the\n                    extent reasonably practicable;\n\n            b. indicate if You modified the Licensed Material and\n               retain an indication of any previous modifications; and\n\n            c. indicate the Licensed Material is licensed under this\n               Public License, and include the text of, or the URI or\n               hyperlink to, this Public License.\n\n       2. You may satisfy the conditions in Section 3(a)(1) in any\n          reasonable manner based on the medium, means, and context in\n          which You Share the Licensed Material. For example, it may be\n          reasonable to satisfy the conditions by providing a URI or\n          hyperlink to a resource that includes the required\n          information.\n       3. If requested by the Licensor, You must remove any of the\n          information required by Section 3(a)(1)(A) to the extent\n          reasonably practicable.\n\n  b. ShareAlike.\n\n     In addition to the conditions in Section 3(a), if You Share\n     Adapted Material You produce, the following conditions also apply.\n\n       1. The Adapter's License You apply must be a Creative Commons\n          license with the same License Elements, this version or\n          later, or a BY-NC-SA Compatible License.\n\n       2. You must include the text of, or the URI or hyperlink to, the\n          Adapter's License You apply. You may satisfy this condition\n          in any reasonable manner based on the medium, means, and\n          context in which You Share Adapted Material.\n\n       3. You may not offer or impose any additional or different terms\n          or conditions on, or apply any Effective Technological\n          Measures to, Adapted Material that restrict exercise of the\n          rights granted under the Adapter's License You apply.\n\n\nSection 4 -- Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that\napply to Your use of the Licensed Material:\n\n  a. for the avoidance of doubt, Section 2(a)(1) grants You the right\n     to extract, reuse, reproduce, and Share all or a substantial\n     portion of the contents of the database for NonCommercial purposes\n     only;\n\n  b. if You include all or a substantial portion of the database\n     contents in a database in which You have Sui Generis Database\n     Rights, then the database in which You have Sui Generis Database\n     Rights (but not its individual contents) is Adapted Material,\n     including for purposes of Section 3(b); and\n\n  c. You must comply with the conditions in Section 3(a) if You Share\n     all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not\nreplace Your obligations under this Public License where the Licensed\nRights include other Copyright and Similar Rights.\n\n\nSection 5 -- Disclaimer of Warranties and Limitation of Liability.\n\n  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE\n     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS\n     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF\n     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,\n     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,\n     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\n     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,\n     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT\n     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT\n     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.\n\n  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE\n     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,\n     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,\n     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,\n     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR\n     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN\n     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR\n     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR\n     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.\n\n  c. The disclaimer of warranties and limitation of liability provided\n     above shall be interpreted in a manner that, to the extent\n     possible, most closely approximates an absolute disclaimer and\n     waiver of all liability.\n\n\nSection 6 -- Term and Termination.\n\n  a. This Public License applies for the term of the Copyright and\n     Similar Rights licensed here. However, if You fail to comply with\n     this Public License, then Your rights under this Public License\n     terminate automatically.\n\n  b. Where Your right to use the Licensed Material has terminated under\n     Section 6(a), it reinstates:\n\n       1. automatically as of the date the violation is cured, provided\n          it is cured within 30 days of Your discovery of the\n          violation; or\n\n       2. upon express reinstatement by the Licensor.\n\n     For the avoidance of doubt, this Section 6(b) does not affect any\n     right the Licensor may have to seek remedies for Your violations\n     of this Public License.\n\n  c. For the avoidance of doubt, the Licensor may also offer the\n     Licensed Material under separate terms or conditions or stop\n     distributing the Licensed Material at any time; however, doing so\n     will not terminate this Public License.\n\n  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public\n     License.\n\n\nSection 7 -- Other Terms and Conditions.\n\n  a. The Licensor shall not be bound by any additional or different\n     terms or conditions communicated by You unless expressly agreed.\n\n  b. Any arrangements, understandings, or agreements regarding the\n     Licensed Material not stated herein are separate from and\n     independent of the terms and conditions of this Public License.\n\n\nSection 8 -- Interpretation.\n\n  a. For the avoidance of doubt, this Public License does not, and\n     shall not be interpreted to, reduce, limit, restrict, or impose\n     conditions on any use of the Licensed Material that could lawfully\n     be made without permission under this Public License.\n\n  b. To the extent possible, if any provision of this Public License is\n     deemed unenforceable, it shall be automatically reformed to the\n     minimum extent necessary to make it enforceable. If the provision\n     cannot be reformed, it shall be severed from this Public License\n     without affecting the enforceability of the remaining terms and\n     conditions.\n\n  c. No term or condition of this Public License will be waived and no\n     failure to comply consented to unless expressly agreed to by the\n     Licensor.\n\n  d. Nothing in this Public License constitutes or may be interpreted\n     as a limitation upon, or waiver of, any privileges and immunities\n     that apply to the Licensor or You, including from the legal\n     processes of any jurisdiction or authority.\n\n=======================================================================\n\nCreative Commons is not a party to its public\nlicenses. Notwithstanding, Creative Commons may elect to apply one of\nits public licenses to material it publishes and in those instances\nwill be considered the “Licensor.” The text of the Creative Commons\npublic licenses is dedicated to the public domain under the CC0 Public\nDomain Dedication. Except for the limited purpose of indicating that\nmaterial is shared under a Creative Commons public license or as\notherwise permitted by the Creative Commons policies published at\ncreativecommons.org/policies, Creative Commons does not authorize the\nuse of the trademark \"Creative Commons\" or any other trademark or logo\nof Creative Commons without its prior written consent including,\nwithout limitation, in connection with any unauthorized modifications\nto any of its public licenses or any other arrangements,\nunderstandings, or agreements concerning use of licensed material. For\nthe avoidance of doubt, this paragraph does not form part of the\npublic licenses.\n\nCreative Commons may be contacted at creativecommons.org.\n\n"
  },
  {
    "path": "Publish/Disclaimer.txt",
    "content": "By using, or simply downloading IntelliTrader (the Software), you understand and accept the following:\n\n\nLicensing\n\nThe Software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International licence. Full terms of the licence can be found by following this link:\n\nhttps://creativecommons.org/licenses/by-nc-sa/4.0/legalcode\n\nA full copy of the license is also included with the download.\n\n\nLimitation Of Liability\n\nIn no event shall liability be accepted for any indirect, incidental, special, consequential or punitive damages, including, without limitation, loss of profits, funds, data, use, goodwill, or other tangible and intangible losses, resulting from:\n\n\t(i) your access to, or use of, or inability to access or use the Software; \n\t(ii) any content of any third party used with the Software; \n\t(iii) any content obtained from the Software; and \n\t(iv) unauthorized access, use or alteration of your transmissions or content, whether based on warranty, contract, tort (including negligence) or any other legal theory, whether or not we have been informed of the possibility of such damage, and even if a remedy set forth herein is found to have failed of its essential purpose.\n\nWe do not refund losses.\n\n\nDisclaimer\n\nYour use of the Software is at your sole risk. The Software is provided on an “AS IS” and “AS AVAILABLE” basis. The Software is provided without warranties of any kind, whether expressed or implied, including, but not limited to, implied warranties of merchantability, fitness for a particular purpose, non-infringement or course of performance.\n\nNo warranty of any kind is expressed or implied, that:\n\n\ta) the Software will function, be secure or operate on any nominated platform; \n\tb) any errors or defects will be corrected; \n\tc) the Software is free of viruses or other harmful components; or \n\td) the results of using the Software will meet your requirements.\n\n\nIf you do not agree with any of the above, please do not download or use the Software."
  },
  {
    "path": "Publish/Help.url",
    "content": "[InternetShortcut]\nURL=https://github.com/jazzonaut/IntelliTrader/wiki\nIconFile=https://assets-cdn.github.com/favicon.ico\nIconIndex=1\n"
  },
  {
    "path": "Publish/IntelliTrader.sh",
    "content": "#!/bin/bash\n\ndotnet bin/IntelliTrader.dll"
  },
  {
    "path": "Publish/License.txt",
    "content": "Attribution-NonCommercial-ShareAlike 4.0 International\n\n=======================================================================\n\nCreative Commons Corporation (\"Creative Commons\") is not a law firm and\ndoes not provide legal services or legal advice. Distribution of\nCreative Commons public licenses does not create a lawyer-client or\nother relationship. Creative Commons makes its licenses and related\ninformation available on an \"as-is\" basis. Creative Commons gives no\nwarranties regarding its licenses, any material licensed under their\nterms and conditions, or any related information. Creative Commons\ndisclaims all liability for damages resulting from their use to the\nfullest extent possible.\n\nUsing Creative Commons Public Licenses\n\nCreative Commons public licenses provide a standard set of terms and\nconditions that creators and other rights holders may use to share\noriginal works of authorship and other material subject to copyright\nand certain other rights specified in the public license below. The\nfollowing considerations are for informational purposes only, are not\nexhaustive, and do not form part of our licenses.\n\n     Considerations for licensors: Our public licenses are\n     intended for use by those authorized to give the public\n     permission to use material in ways otherwise restricted by\n     copyright and certain other rights. Our licenses are\n     irrevocable. Licensors should read and understand the terms\n     and conditions of the license they choose before applying it.\n     Licensors should also secure all rights necessary before\n     applying our licenses so that the public can reuse the\n     material as expected. Licensors should clearly mark any\n     material not subject to the license. This includes other CC-\n     licensed material, or material used under an exception or\n     limitation to copyright. More considerations for licensors:\n\twiki.creativecommons.org/Considerations_for_licensors\n\n     Considerations for the public: By using one of our public\n     licenses, a licensor grants the public permission to use the\n     licensed material under specified terms and conditions. If\n     the licensor's permission is not necessary for any reason--for\n     example, because of any applicable exception or limitation to\n     copyright--then that use is not regulated by the license. Our\n     licenses grant only permissions under copyright and certain\n     other rights that a licensor has authority to grant. Use of\n     the licensed material may still be restricted for other\n     reasons, including because others have copyright or other\n     rights in the material. A licensor may make special requests,\n     such as asking that all changes be marked or described.\n     Although not required by our licenses, you are encouraged to\n     respect those requests where reasonable. More considerations\n     for the public: \n\twiki.creativecommons.org/Considerations_for_licensees\n\n=======================================================================\n\nCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International\nPublic License\n\nBy exercising the Licensed Rights (defined below), You accept and agree\nto be bound by the terms and conditions of this Creative Commons\nAttribution-NonCommercial-ShareAlike 4.0 International Public License\n(\"Public License\"). To the extent this Public License may be\ninterpreted as a contract, You are granted the Licensed Rights in\nconsideration of Your acceptance of these terms and conditions, and the\nLicensor grants You such rights in consideration of benefits the\nLicensor receives from making the Licensed Material available under\nthese terms and conditions.\n\n\nSection 1 -- Definitions.\n\n  a. Adapted Material means material subject to Copyright and Similar\n     Rights that is derived from or based upon the Licensed Material\n     and in which the Licensed Material is translated, altered,\n     arranged, transformed, or otherwise modified in a manner requiring\n     permission under the Copyright and Similar Rights held by the\n     Licensor. For purposes of this Public License, where the Licensed\n     Material is a musical work, performance, or sound recording,\n     Adapted Material is always produced where the Licensed Material is\n     synched in timed relation with a moving image.\n\n  b. Adapter's License means the license You apply to Your Copyright\n     and Similar Rights in Your contributions to Adapted Material in\n     accordance with the terms and conditions of this Public License.\n\n  c. BY-NC-SA Compatible License means a license listed at\n     creativecommons.org/compatiblelicenses, approved by Creative\n     Commons as essentially the equivalent of this Public License.\n\n  d. Copyright and Similar Rights means copyright and/or similar rights\n     closely related to copyright including, without limitation,\n     performance, broadcast, sound recording, and Sui Generis Database\n     Rights, without regard to how the rights are labeled or\n     categorized. For purposes of this Public License, the rights\n     specified in Section 2(b)(1)-(2) are not Copyright and Similar\n     Rights.\n\n  e. Effective Technological Measures means those measures that, in the\n     absence of proper authority, may not be circumvented under laws\n     fulfilling obligations under Article 11 of the WIPO Copyright\n     Treaty adopted on December 20, 1996, and/or similar international\n     agreements.\n\n  f. Exceptions and Limitations means fair use, fair dealing, and/or\n     any other exception or limitation to Copyright and Similar Rights\n     that applies to Your use of the Licensed Material.\n\n  g. License Elements means the license attributes listed in the name\n     of a Creative Commons Public License. The License Elements of this\n     Public License are Attribution, NonCommercial, and ShareAlike.\n\n  h. Licensed Material means the artistic or literary work, database,\n     or other material to which the Licensor applied this Public\n     License.\n\n  i. Licensed Rights means the rights granted to You subject to the\n     terms and conditions of this Public License, which are limited to\n     all Copyright and Similar Rights that apply to Your use of the\n     Licensed Material and that the Licensor has authority to license.\n\n  j. Licensor means the individual(s) or entity(ies) granting rights\n     under this Public License.\n\n  k. NonCommercial means not primarily intended for or directed towards\n     commercial advantage or monetary compensation. For purposes of\n     this Public License, the exchange of the Licensed Material for\n     other material subject to Copyright and Similar Rights by digital\n     file-sharing or similar means is NonCommercial provided there is\n     no payment of monetary compensation in connection with the\n     exchange.\n\n  l. Share means to provide material to the public by any means or\n     process that requires permission under the Licensed Rights, such\n     as reproduction, public display, public performance, distribution,\n     dissemination, communication, or importation, and to make material\n     available to the public including in ways that members of the\n     public may access the material from a place and at a time\n     individually chosen by them.\n\n  m. Sui Generis Database Rights means rights other than copyright\n     resulting from Directive 96/9/EC of the European Parliament and of\n     the Council of 11 March 1996 on the legal protection of databases,\n     as amended and/or succeeded, as well as other essentially\n     equivalent rights anywhere in the world.\n\n  n. You means the individual or entity exercising the Licensed Rights\n     under this Public License. Your has a corresponding meaning.\n\n\nSection 2 -- Scope.\n\n  a. License grant.\n\n       1. Subject to the terms and conditions of this Public License,\n          the Licensor hereby grants You a worldwide, royalty-free,\n          non-sublicensable, non-exclusive, irrevocable license to\n          exercise the Licensed Rights in the Licensed Material to:\n\n            a. reproduce and Share the Licensed Material, in whole or\n               in part, for NonCommercial purposes only; and\n\n            b. produce, reproduce, and Share Adapted Material for\n               NonCommercial purposes only.\n\n       2. Exceptions and Limitations. For the avoidance of doubt, where\n          Exceptions and Limitations apply to Your use, this Public\n          License does not apply, and You do not need to comply with\n          its terms and conditions.\n\n       3. Term. The term of this Public License is specified in Section\n          6(a).\n\n       4. Media and formats; technical modifications allowed. The\n          Licensor authorizes You to exercise the Licensed Rights in\n          all media and formats whether now known or hereafter created,\n          and to make technical modifications necessary to do so. The\n          Licensor waives and/or agrees not to assert any right or\n          authority to forbid You from making technical modifications\n          necessary to exercise the Licensed Rights, including\n          technical modifications necessary to circumvent Effective\n          Technological Measures. For purposes of this Public License,\n          simply making modifications authorized by this Section 2(a)\n          (4) never produces Adapted Material.\n\n       5. Downstream recipients.\n\n            a. Offer from the Licensor -- Licensed Material. Every\n               recipient of the Licensed Material automatically\n               receives an offer from the Licensor to exercise the\n               Licensed Rights under the terms and conditions of this\n               Public License.\n\n            b. Additional offer from the Licensor -- Adapted Material.\n               Every recipient of Adapted Material from You\n               automatically receives an offer from the Licensor to\n               exercise the Licensed Rights in the Adapted Material\n               under the conditions of the Adapter's License You apply.\n\n            c. No downstream restrictions. You may not offer or impose\n               any additional or different terms or conditions on, or\n               apply any Effective Technological Measures to, the\n               Licensed Material if doing so restricts exercise of the\n               Licensed Rights by any recipient of the Licensed\n               Material.\n\n       6. No endorsement. Nothing in this Public License constitutes or\n          may be construed as permission to assert or imply that You\n          are, or that Your use of the Licensed Material is, connected\n          with, or sponsored, endorsed, or granted official status by,\n          the Licensor or others designated to receive attribution as\n          provided in Section 3(a)(1)(A)(i).\n\n  b. Other rights.\n\n       1. Moral rights, such as the right of integrity, are not\n          licensed under this Public License, nor are publicity,\n          privacy, and/or other similar personality rights; however, to\n          the extent possible, the Licensor waives and/or agrees not to\n          assert any such rights held by the Licensor to the limited\n          extent necessary to allow You to exercise the Licensed\n          Rights, but not otherwise.\n\n       2. Patent and trademark rights are not licensed under this\n          Public License.\n\n       3. To the extent possible, the Licensor waives any right to\n          collect royalties from You for the exercise of the Licensed\n          Rights, whether directly or through a collecting society\n          under any voluntary or waivable statutory or compulsory\n          licensing scheme. In all other cases the Licensor expressly\n          reserves any right to collect such royalties, including when\n          the Licensed Material is used other than for NonCommercial\n          purposes.\n\n\nSection 3 -- License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the\nfollowing conditions.\n\n  a. Attribution.\n\n       1. If You Share the Licensed Material (including in modified\n          form), You must:\n\n            a. retain the following if it is supplied by the Licensor\n               with the Licensed Material:\n\n                 i. identification of the creator(s) of the Licensed\n                    Material and any others designated to receive\n                    attribution, in any reasonable manner requested by\n                    the Licensor (including by pseudonym if\n                    designated);\n\n                ii. a copyright notice;\n\n               iii. a notice that refers to this Public License;\n\n                iv. a notice that refers to the disclaimer of\n                    warranties;\n\n                 v. a URI or hyperlink to the Licensed Material to the\n                    extent reasonably practicable;\n\n            b. indicate if You modified the Licensed Material and\n               retain an indication of any previous modifications; and\n\n            c. indicate the Licensed Material is licensed under this\n               Public License, and include the text of, or the URI or\n               hyperlink to, this Public License.\n\n       2. You may satisfy the conditions in Section 3(a)(1) in any\n          reasonable manner based on the medium, means, and context in\n          which You Share the Licensed Material. For example, it may be\n          reasonable to satisfy the conditions by providing a URI or\n          hyperlink to a resource that includes the required\n          information.\n       3. If requested by the Licensor, You must remove any of the\n          information required by Section 3(a)(1)(A) to the extent\n          reasonably practicable.\n\n  b. ShareAlike.\n\n     In addition to the conditions in Section 3(a), if You Share\n     Adapted Material You produce, the following conditions also apply.\n\n       1. The Adapter's License You apply must be a Creative Commons\n          license with the same License Elements, this version or\n          later, or a BY-NC-SA Compatible License.\n\n       2. You must include the text of, or the URI or hyperlink to, the\n          Adapter's License You apply. You may satisfy this condition\n          in any reasonable manner based on the medium, means, and\n          context in which You Share Adapted Material.\n\n       3. You may not offer or impose any additional or different terms\n          or conditions on, or apply any Effective Technological\n          Measures to, Adapted Material that restrict exercise of the\n          rights granted under the Adapter's License You apply.\n\n\nSection 4 -- Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that\napply to Your use of the Licensed Material:\n\n  a. for the avoidance of doubt, Section 2(a)(1) grants You the right\n     to extract, reuse, reproduce, and Share all or a substantial\n     portion of the contents of the database for NonCommercial purposes\n     only;\n\n  b. if You include all or a substantial portion of the database\n     contents in a database in which You have Sui Generis Database\n     Rights, then the database in which You have Sui Generis Database\n     Rights (but not its individual contents) is Adapted Material,\n     including for purposes of Section 3(b); and\n\n  c. You must comply with the conditions in Section 3(a) if You Share\n     all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not\nreplace Your obligations under this Public License where the Licensed\nRights include other Copyright and Similar Rights.\n\n\nSection 5 -- Disclaimer of Warranties and Limitation of Liability.\n\n  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE\n     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS\n     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF\n     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,\n     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,\n     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\n     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,\n     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT\n     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT\n     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.\n\n  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE\n     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,\n     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,\n     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,\n     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR\n     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN\n     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR\n     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR\n     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.\n\n  c. The disclaimer of warranties and limitation of liability provided\n     above shall be interpreted in a manner that, to the extent\n     possible, most closely approximates an absolute disclaimer and\n     waiver of all liability.\n\n\nSection 6 -- Term and Termination.\n\n  a. This Public License applies for the term of the Copyright and\n     Similar Rights licensed here. However, if You fail to comply with\n     this Public License, then Your rights under this Public License\n     terminate automatically.\n\n  b. Where Your right to use the Licensed Material has terminated under\n     Section 6(a), it reinstates:\n\n       1. automatically as of the date the violation is cured, provided\n          it is cured within 30 days of Your discovery of the\n          violation; or\n\n       2. upon express reinstatement by the Licensor.\n\n     For the avoidance of doubt, this Section 6(b) does not affect any\n     right the Licensor may have to seek remedies for Your violations\n     of this Public License.\n\n  c. For the avoidance of doubt, the Licensor may also offer the\n     Licensed Material under separate terms or conditions or stop\n     distributing the Licensed Material at any time; however, doing so\n     will not terminate this Public License.\n\n  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public\n     License.\n\n\nSection 7 -- Other Terms and Conditions.\n\n  a. The Licensor shall not be bound by any additional or different\n     terms or conditions communicated by You unless expressly agreed.\n\n  b. Any arrangements, understandings, or agreements regarding the\n     Licensed Material not stated herein are separate from and\n     independent of the terms and conditions of this Public License.\n\n\nSection 8 -- Interpretation.\n\n  a. For the avoidance of doubt, this Public License does not, and\n     shall not be interpreted to, reduce, limit, restrict, or impose\n     conditions on any use of the Licensed Material that could lawfully\n     be made without permission under this Public License.\n\n  b. To the extent possible, if any provision of this Public License is\n     deemed unenforceable, it shall be automatically reformed to the\n     minimum extent necessary to make it enforceable. If the provision\n     cannot be reformed, it shall be severed from this Public License\n     without affecting the enforceability of the remaining terms and\n     conditions.\n\n  c. No term or condition of this Public License will be waived and no\n     failure to comply consented to unless expressly agreed to by the\n     Licensor.\n\n  d. Nothing in this Public License constitutes or may be interpreted\n     as a limitation upon, or waiver of, any privileges and immunities\n     that apply to the Licensor or You, including from the legal\n     processes of any jurisdiction or authority.\n\n=======================================================================\n\nCreative Commons is not a party to its public\nlicenses. Notwithstanding, Creative Commons may elect to apply one of\nits public licenses to material it publishes and in those instances\nwill be considered the “Licensor.” The text of the Creative Commons\npublic licenses is dedicated to the public domain under the CC0 Public\nDomain Dedication. Except for the limited purpose of indicating that\nmaterial is shared under a Creative Commons public license or as\notherwise permitted by the Creative Commons policies published at\ncreativecommons.org/policies, Creative Commons does not authorize the\nuse of the trademark \"Creative Commons\" or any other trademark or logo\nof Creative Commons without its prior written consent including,\nwithout limitation, in connection with any unauthorized modifications\nto any of its public licenses or any other arrangements,\nunderstandings, or agreements concerning use of licensed material. For\nthe avoidance of doubt, this paragraph does not form part of the\npublic licenses.\n\nCreative Commons may be contacted at creativecommons.org.\n\n"
  },
  {
    "path": "Publish/config/backtesting.json",
    "content": "﻿{\n  \"Backtesting\": {\n    \"Enabled\": false,\n    \"Replay\": false,\n    \"ReplayOutput\": true,\n    \"ReplaySpeed\": 50,\n    \"ReplayStartIndex\": null,\n    \"ReplayEndIndex\": null,\n    \"DeleteLogs\": true,\n    \"DeleteAccountData\": true,\n    \"CopyAccountDataPath\": null,\n    \"TradingSpeedEasing\": 0,\n    \"TradingRulesSpeedEasing\": 0,\n    \"SignalRulesSpeedEasing\": 0,\n    \"SnapshotsInterval\": 1,\n    \"SnapshotsPath\": \"data/backtesting\"\n  }\n}"
  },
  {
    "path": "Publish/config/core.json",
    "content": "{\n  \"Core\": {\n    \"DebugMode\": false,\n    \"PasswordProtected\": true,\n    \"Password\": \"b84967c4f073b71405404f3719c788cd\",\n    \"InstanceName\": \"Main\",\n    \"TimezoneOffset\": 1,\n    \"HealthCheckEnabled\": true,\n    \"HealthCheckInterval\": 180,\n    \"HealthCheckSuspendTradingTimeout\": 900,\n    \"HealthCheckFailuresToRestartServices\": 5\n  }\n}"
  },
  {
    "path": "Publish/config/exchange.json",
    "content": "{\n  \"Exchange\": {\n    \"KeysPath\": \"data/keys.bin\",\n    \"RateLimitOccurences\": 40,\n    \"RateLimitTimeframe\": 10\n  }\n}"
  },
  {
    "path": "Publish/config/logging.json",
    "content": "{\n  \"Logging\": {\n    \"Enabled\": true,\n    \"MinimumLevel\": {\n      \"Default\": \"Verbose\",\n      \"Override\": {\n        \"System\": \"Warning\",\n        \"Microsoft\": \"Warning\"\n      }\n    },\n    \"WriteTo\": [\n      {\n        \"Name\": \"Logger\",\n        \"Args\": {\n          \"configureLogger\": {\n            \"WriteTo\": [\n              {\n                \"Name\": \"Console\",\n                \"Args\": {\n                  \"outputTemplate\": \"{Timestamp:HH:mm:ss} [{Level:u3}] {Message}{NewLine}{Exception}\",\n                  \"theme\": \"Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Literate, Serilog.Sinks.Console\",\n                  \"restrictedToMinimumLevel\": \"Information\"\n                }\n              },\n              {\n                \"Name\": \"RollingFile\",\n                \"Args\": {\n                  \"outputTemplate\": \"[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] {Message}{NewLine}{Exception}\",\n                  \"pathFormat\": \"log/{Date}-general.txt\",\n                  \"retainedFileCountLimit\": 1000\n                }\n              }\n            ],\n            \"Filter\": [\n              {\n                \"Name\": \"ByIncludingOnly\",\n                \"Args\": {\n                  \"expression\": \"Trade is null\"\n                }\n              }\n            ]\n          }\n        }\n      },\n      {\n        \"Name\": \"Logger\",\n        \"Args\": {\n          \"configureLogger\": {\n            \"WriteTo\": [\n              {\n                \"Name\": \"RollingFile\",\n                \"Args\": {\n                  \"outputTemplate\": \"[{Timestamp:HH:mm:ss.fff}] {Message}{NewLine}\",\n                  \"pathFormat\": \"log/{Date}-trades.txt\",\n                  \"retainedFileCountLimit\": 1000\n                }\n              }\n            ],\n            \"Filter\": [\n              {\n                \"Name\": \"ByIncludingOnly\",\n                \"Args\": {\n                  \"expression\": \"Trade is not null\"\n                }\n              }\n            ]\n          }\n        }\n      }\n    ]\n  }\n}"
  },
  {
    "path": "Publish/config/notification.json",
    "content": "{\n  \"Notification\": {\n    \"Enabled\": false,\n    \"TelegramEnabled\": true,\n    \"TelegramBotToken\": \"\",\n    \"TelegramChatId\": 0,\n    \"TelegramAlertsEnabled\": true\n  }\n}"
  },
  {
    "path": "Publish/config/paths.json",
    "content": "﻿{\n  \"Paths\": {\n    \"Core\": \"core.json\",\n    \"Logging\": \"logging.json\",\n    \"Trading\": \"trading.json\",\n    \"Exchange\": \"exchange.json\",\n    \"Signals\": \"signals.json\",\n    \"Rules\": \"rules.json\",\n    \"Notification\": \"notification.json\",\n    \"Web\": \"web.json\",\n    \"Backtesting\": \"backtesting.json\"\n  }\n}"
  },
  {
    "path": "Publish/config/rules.json",
    "content": "{\n  \"Rules\": {\n    \"Modules\": [\n      {\n        \"Module\": \"Signals\",\n        \"Configuration\": {\n          \"ProcessingMode\": \"AllMatches\",\n          \"CheckInterval\": 0.1\n        },\n        \"Entries\": [\n          {\n            \"Enabled\": false,\n            \"Name\": \"Buy-Arbitrage\",\n            \"Action\": \"Arbitrage\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"MinArbitrage\": 4\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Safe\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinVolatility\": 2.5,\n                \"MaxVolatility\": 10,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.25,\n                \"MinVolume\": 100000,\n                \"MaxPriceChange\": 6\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinRating\": 0.1,\n                \"MinPriceChange\": 1.5,\n                \"MaxPriceChange\": 12\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 5,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": -0.35,\n                \"MaxGlobalRating\": 1.0,\n                \"MaxSpread\": 0.25,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Bull\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinVolatility\": 3,\n                \"MaxVolatility\": 12,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.25,\n                \"MinVolume\": 100000,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinRating\": 0.1,\n                \"MinPriceChange\": 2,\n                \"MaxPriceChange\": 12\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 4,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": 0.25,\n                \"MaxSpread\": 0.35,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": false,\n            \"Name\": \"Buy-TUSDT\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-5m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"MinGlobalRating\": -0.28,\n                \"MaxGlobalRating\": -0.15,\n                \"MaxSpread\": 0.35,\n                \"Pairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Volume-Spike\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MinRating\": 0.4,\n                \"MinPriceChange\": 1,\n                \"MaxPriceChange\": 8,\n                \"MinVolumeChange\": 500,\n                \"MaxVolatility\": 12\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.4,\n                \"MinPriceChange\": 1.5,\n                \"MaxPriceChange\": 9,\n                \"MinVolumeChange\": 200\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.25,\n                \"MinVolume\": 200000,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinPriceChange\": 1,\n                \"MaxPriceChange\": 10\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 2,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": -0.10,\n                \"MaxGlobalRating\": 1.0,\n                \"MaxSpread\": 1,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Buy-Pump\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-1m\",\n                \"MinVolumeChange\": 20,\n                \"MaxVolumeChange\": 200,\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-5m\",\n                \"MinRating\": 0.30,\n                \"MinPriceChange\": 3,\n                \"MinVolumeChange\": 0,\n                \"MaxPriceChange\": 10\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.30\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.0,\n                \"MinVolume\": 100000,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinPriceChange\": 3,\n                \"MaxPriceChange\": 10\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinPriceChange\": 4,\n                \"MaxPriceChange\": 20\n              },\n              {\n                \"MinGlobalRating\": -0.30,\n                \"MaxGlobalRating\": 1.0,\n                \"MaxSpread\": 0.6\n              }\n            ],\n            \"Trailing\": {\n              \"Enabled\": true,\n              \"MinDuration\": 10,\n              \"MaxDuration\": 60,\n              \"StartConditions\": [\n                {\n                  \"Signal\": \"TV-5m\",\n                  \"MinVolumeChange\": 200\n                }\n              ]\n            }\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Safe\",\n            \"Action\": \"Swap\",\n            \"Modifiers\": {\n              \"CostMultiplier\": 1\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MinRating\": 0.20,\n                \"MaxRating\": 0.35,\n                \"MaxPriceChange\": 5\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MinRating\": 0.10,\n                \"MaxRating\": 0.25,\n                \"MinVolume\": 400000,\n                \"MinRatingChange\": 0,\n                \"MaxPriceChange\": 6\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MinRating\": 0.10,\n                \"MaxRating\": 0.25,\n                \"MinPriceChange\": 1,\n                \"MaxPriceChange\": 8\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MinRatingChange\": 0,\n                \"MinPriceChange\": 2,\n                \"MaxPriceChange\": 12\n              },\n              {\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1,\n                \"MaxSpread\": 0.35,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"Module\": \"Trading\",\n        \"Configuration\": {\n          \"ProcessingMode\": \"AllMatches\",\n          \"CheckInterval\": 0.1\n        },\n        \"Entries\": [\n          {\n            \"Enabled\": true,\n            \"Name\": \"TUSD-DCA\",\n            \"Modifiers\": {\n              \"BuyEnabled\": true,\n              \"BuyDCAEnabled\": true,\n              \"SellMargin\": 0.20,\n              \"SellTrailing\": 0.15,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.40,\n                  \"BuySamePairTimeout\": 0,\n                  \"BuyTrailing\": -0.20,\n                  \"BuyTrailingStopMargin\": 1.00,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.15\n                },\n                {\n                  \"Margin\": -1.25,\n                  \"BuySamePairTimeout\": 180,\n                  \"BuyTrailing\": -0.20,\n                  \"BuyTrailingStopMargin\": 1.50,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15\n                },\n                {\n                  \"Margin\": -2.50,\n                  \"BuySamePairTimeout\": 300,\n                  \"BuyTrailing\": -0.25,\n                  \"BuyTrailingStopMargin\": 2.00,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.10\n                },\n                {\n                  \"Margin\": -5.50,\n                  \"BuySamePairTimeout\": 1800,\n                  \"BuyTrailing\": -0.25,\n                  \"BuyTrailingStopMargin\": 2.00,\n                  \"BuyTrailingStopAction\": \"Cancel\",\n                  \"SellMargin\": 0.05,\n                  \"SellTrailing\": 0.10\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -1.00,\n                \"MaxGlobalRating\": -0.10,\n                \"Pairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Apocalypse\",\n            \"Modifiers\": {\n              \"BuyEnabled\": false,\n              \"BuyDCAEnabled\": false,\n              \"SellMargin\": -0.50,\n              \"SellTrailing\": 0,\n              \"SellDCAMargin\": -0.50,\n              \"SellDCATrailing\": 0\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -1,\n                \"MaxGlobalRating\": -0.40\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Bear\",\n            \"Modifiers\": {\n              \"BuyDCAEnabled\": false,\n              \"BuyTrailing\": -0.45,\n              \"SellMargin\": 0.40,\n              \"SellTrailing\": 0.25,\n              \"SellTrailingStopMargin\": 0.30,\n              \"MaxPairs\": 4,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -4,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.35,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -8,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.10,\n                  \"BuyTrailing\": -0.55,\n                  \"BuySamePairTimeout\": 600\n                },\n                {\n                  \"Margin\": -15,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.10,\n                  \"BuyTrailing\": -1,\n                  \"BuySamePairTimeout\": 1200\n                },\n                {\n                  \"Margin\": -20,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": -1.5,\n                  \"BuySamePairTimeout\": 2880\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -0.40,\n                \"MaxGlobalRating\": -0.15,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"BuyDCAEnabled\": false,\n            \"Name\": \"Boring\",\n            \"Modifiers\": {\n              \"BuyTrailing\": -0.35,\n              \"SellMargin\": 0.50,\n              \"SellTrailing\": 0.35,\n              \"SellTrailingStopMargin\": 0.40,\n              \"MaxPairs\": 5,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -2.5,\n                  \"SellMargin\": 0.30,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.30,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -5,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.45,\n                  \"BuySamePairTimeout\": 300\n                },\n                {\n                  \"Margin\": -8,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.10,\n                  \"SellTrailingStopMargin\": 0.10,\n                  \"BuyTrailing\": -0.75,\n                  \"BuySamePairTimeout\": 900\n                },\n                {\n                  \"Margin\": -12,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": -1.5,\n                  \"BuySamePairTimeout\": 1800\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -0.15,\n                \"MaxGlobalRating\": 0.15,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Bull\",\n            \"Modifiers\": {\n              \"BuyTrailing\": -0.25,\n              \"SellMargin\": 0.60,\n              \"SellTrailing\": 0.45,\n              \"SellTrailingStopMargin\": 0.50,\n              \"MaxPairs\": 6,\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.35,\n                  \"SellMargin\": 0.40,\n                  \"SellTrailing\": 0.35,\n                  \"SellTrailingStopMargin\": 0.35,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -2,\n                  \"SellMargin\": 0.35,\n                  \"SellTrailing\": 0.30,\n                  \"SellTrailingStopMargin\": 0.30,\n                  \"BuyTrailing\": -0.35,\n                  \"BuySamePairTimeout\": 180\n                },\n                {\n                  \"Margin\": -7,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15,\n                  \"SellTrailingStopMargin\": 0.15,\n                  \"BuyTrailing\": -0.55,\n                  \"BuySamePairTimeout\": 600\n                },\n                {\n                  \"Margin\": -12,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": -1.5,\n                  \"BuySamePairTimeout\": 1400\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": 0.15,\n                \"MaxGlobalRating\": 1.00\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"DCA\",\n            \"Modifiers\": {\n              \"BuyDCAEnabled\": false\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0.30\n              },\n              {\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"SuperBull-HP-DCA\",\n            \"Modifiers\": {\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.25,\n                  \"SellMargin\": 0.25,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.25,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -0.75,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.45,\n                  \"BuySamePairTimeout\": 300\n                },\n                {\n                  \"Margin\": -1.50,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15,\n                  \"SellTrailingStopMargin\": 0.15,\n                  \"BuyTrailing\": -0.65,\n                  \"BuySamePairTimeout\": 600\n                },\n                {\n                  \"Margin\": -4.0,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": 2.0,\n                  \"BuySamePairTimeout\": 1200\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"MinGlobalRating\": 0.30\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Bull-HP-DCA\",\n            \"Modifiers\": {\n              \"DCALevels\": [\n                {\n                  \"Margin\": -0.35,\n                  \"SellMargin\": 0.25,\n                  \"SellTrailing\": 0.25,\n                  \"SellTrailingStopMargin\": 0.25,\n                  \"BuyTrailing\": -0.25,\n                  \"BuySamePairTimeout\": 0\n                },\n                {\n                  \"Margin\": -1.5,\n                  \"SellMargin\": 0.20,\n                  \"SellTrailing\": 0.20,\n                  \"SellTrailingStopMargin\": 0.20,\n                  \"BuyTrailing\": -0.35,\n                  \"BuySamePairTimeout\": 180\n                },\n                {\n                  \"Margin\": -5.5,\n                  \"SellMargin\": 0.15,\n                  \"SellTrailing\": 0.15,\n                  \"SellTrailingStopMargin\": 0.15,\n                  \"BuyTrailing\": -0.55,\n                  \"BuySamePairTimeout\": 300\n                },\n                {\n                  \"Margin\": -10,\n                  \"SellMargin\": 0.10,\n                  \"SellTrailing\": 0.05,\n                  \"SellTrailingStopMargin\": 0.05,\n                  \"BuyTrailing\": 2.0,\n                  \"BuySamePairTimeout\": 1200\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0.40\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0.30\n              },\n              {\n                \"MinGlobalRating\": 0.20,\n                \"MaxGlobalRating\": 0.30\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Only-Bull\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\" ],\n              \"SwapTimeout\": 1800\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 2,\n                \"MinGlobalRating\": 0.30,\n                \"MaxGlobalRating\": 1.00\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Money-Pump\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Pump\" ],\n              \"SwapTimeout\": 300\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 0,\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Small-Bag\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Volume-Spike\", \"Buy-Safe\" ],\n              \"SwapTimeout\": 900\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": -0.30\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": -0.30\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 1,\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Medium-Bag\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\" ],\n              \"SwapTimeout\": 3600\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": -0.2\n              },\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": -0.2\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MaxDCALevel\": 2,\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Big-Bag\",\n            \"Modifiers\": {\n              \"BuyDCAEnabled\": false,\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Swap-Safe\" ],\n              \"SwapTimeout\": 7200\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-15m\",\n                \"MaxRating\": -0.2\n              },\n              {\n                \"Signal\": \"TV-1h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-4h\",\n                \"MaxRating\": 0\n              },\n              {\n                \"Signal\": \"TV-1d\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MinDCALevel\": 3\n              },\n              {\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-Run-Forest-Run\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\", \"Swap-Safe\" ],\n              \"SwapTimeout\": 300\n            },\n            \"Conditions\": [\n              {\n                \"Signal\": \"TV-5m\",\n                \"MaxRating\": 0\n              },\n              {\n                \"MinGlobalRating\": -0.20,\n                \"MaxGlobalRating\": 1.00,\n                \"MinMarginChange\": 3,\n                \"NotPairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Swap-TUSDT\",\n            \"Modifiers\": {\n              \"SwapEnabled\": true,\n              \"SwapSignalRules\": [ \"Buy-Safe\", \"Buy-Volume-Spike\" ],\n              \"SwapTimeout\": 300\n            },\n            \"Conditions\": [\n              {\n                \"MinGlobalRating\": -0.10,\n                \"MaxGlobalRating\": 1.00,\n                \"Pairs\": [\n                  \"TUSDBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Exclude-BNB\",\n            \"Modifiers\": {\n              \"BuyEnabled\": false,\n              \"BuyDCAEnabled\": false,\n              \"SellEnabled\": false\n            },\n            \"Conditions\": [\n              {\n                \"Pairs\": [\n                  \"BNBBTC\"\n                ]\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"BNB-Top-Up\",\n            \"Modifiers\": {\n              \"BuyEnabled\": true,\n              \"BuyDCAEnabled\": true,\n              \"BuyDCASamePairTimeout\": 0,\n              \"BuyMaxCost\": 0.003,\n              \"RepeatLastDCALevel\": true,\n              \"DCALevels\": [\n                {\n                  \"Margin\": 100\n                }\n              ]\n            },\n            \"Conditions\": [\n              {\n                \"Pairs\": [ \"BNBBTC\" ],\n                \"MaxAmount\": 1\n              }\n            ]\n          },\n          {\n            \"Enabled\": true,\n            \"Name\": \"Exclude-Pairs\",\n            \"Modifiers\": {\n              \"BuyEnabled\": false\n            },\n            \"Conditions\": [\n              {\n                \"Pairs\": [\n                  \"ADABTC\",\n                  \"BCHBTC\",\n                  \"ETHBTC\",\n                  \"XRPBTC\",\n                  \"LTCBTC\",\n                  \"EOSBTC\",\n                  \"XMRBTC\",\n                  \"ZECBTC\",\n                  \"ICXBTC\",\n                  \"DASHBTC\",\n                  \"XLMBTC\",\n                  \"OAXBTC\",\n                  \"RCNBTC\",\n                  \"QSPBTC\",\n                  \"SUBBTC\",\n                  \"BNTBTC\",\n                  \"AEBTC\",\n                  \"TNBBTC\",\n                  \"XEMBTC\",\n                  \"TNTBTC\",\n                  \"TRXBTC\",\n                  \"IOTABTC\",\n                  \"AMBBTC\",\n                  \"DLTBTC\",\n                  \"CDTBTC\",\n                  \"CNDBTC\",\n                  \"CHATBTC\",\n                  \"REQBTC\",\n                  \"CVCBTC\",\n                  \"DNTBTC\",\n                  \"IOTXBTC\",\n                  \"RPXBTC\",\n                  \"VIBBTC\"\n                ],\n                \"MaxGlobalRating\": 0.3\n              }\n            ]\n          },\n          {\n            \"Enabled\": false,\n            \"Name\": \"Arbitrage\",\n            \"Modifiers\": {\n              \"BuyEnabled\": true,\n              \"ArbitrageEnabled\": true,\n              \"ArbitrageMarkets\": [ \"ETH\", \"BNB\" ],\n              \"ArbitrageBuyMultiplier\": 0.985,\n              \"ArbitrageSellMultiplier\": 0.985,\n              \"ArbitrageSignalRules\": [ \"Buy-Arbitrage\" ]\n            },\n            \"Conditions\": [\n              {\n                \"MinArbitrage\": 3\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "Publish/config/signals.json",
    "content": "{\n  \"Signals\": {\n    \"Enabled\": true,\n    \"GlobalRatingSignals\": [\n      \"TV-5m\",\n      \"TV-15m\",\n      \"TV-1h\"\n    ],\n    \"Definitions\": [\n      {\n        \"Name\": \"TV-1m\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 1,\n          \"SignalPeriod\": 1,\n          \"VolatilityPeriod\": \"Day\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-5m\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 3,\n          \"SignalPeriod\": 5,\n          \"VolatilityPeriod\": \"Day\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-15m\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 5,\n          \"SignalPeriod\": 15,\n          \"VolatilityPeriod\": \"Week\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-1h\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 7,\n          \"SignalPeriod\": 60,\n          \"VolatilityPeriod\": \"Month\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-4h\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 15,\n          \"SignalPeriod\": 240,\n          \"VolatilityPeriod\": \"Month\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      },\n      {\n        \"Name\": \"TV-1d\",\n        \"Receiver\": \"TradingViewCryptoSignalReceiver\",\n        \"Configuration\": {\n          \"PollingInterval\": 60,\n          \"SignalPeriod\": 1440,\n          \"VolatilityPeriod\": \"Month\",\n          \"RequestUrl\": \"https://scanner.tradingview.com/crypto/scan\",\n          \"RequestData\": \"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"\n        }\n      }\n    ]\n  }\n}"
  },
  {
    "path": "Publish/config/trading.json",
    "content": "{\n  \"Trading\": {\n    \"Enabled\": true,\n    \"Market\": \"BTC\",\n    \"Exchange\": \"Binance\",\n    \"MaxPairs\": 5,\n    \"MinCost\": 0.000999,\n    \"TradePriceType\": \"Bid\",\n    \"ExcludedPairs\": [],\n    \"BuyEnabled\": true,\n    \"BuyType\": \"Market\",\n    \"BuyMaxCost\": 0.03,\n    \"BuyMultiplier\": 1,\n    \"BuyMinBalance\": 0,\n    \"BuySamePairTimeout\": 15,\n    \"BuyTrailing\": -0.25,\n    \"BuyTrailingStopMargin\": 1.35,\n    \"BuyTrailingStopAction\": \"Buy\",\n    \"BuyDCAEnabled\": true,\n    \"BuyDCAMultiplier\": 1,\n    \"BuyDCAMinBalance\": 0,\n    \"BuyDCASamePairTimeout\": 180,\n    \"BuyDCATrailing\": -0.25,\n    \"BuyDCATrailingStopMargin\": 2,\n    \"BuyDCATrailingStopAction\": \"Buy\",\n    \"SellEnabled\": true,\n    \"SellType\": \"Market\",\n    \"SellMargin\": 1.0,\n    \"SellTrailing\": 0.65,\n    \"SellTrailingStopMargin\": 0.7,\n    \"SellTrailingStopAction\": \"Sell\",\n    \"SellStopLossEnabled\": false,\n    \"SellStopLossAfterDCA\": false,\n    \"SellStopLossMinAge\": 0,\n    \"SellStopLossMargin\": -3,\n    \"SellDCAMargin\": 0.5,\n    \"SellDCATrailing\": 0.35,\n    \"SellDCATrailingStopMargin\": 0.5,\n    \"SellDCATrailingStopAction\": \"Sell\",\n    \"RepeatLastDCALevel\": false,\n    \"DCALevels\": [],\n    \"TradingCheckInterval\": 0.1,\n    \"AccountRefreshInterval\": 360,\n    \"AccountInitialBalance\": 1,\n    \"AccountInitialBalanceDate\": \"2018-04-08T00:00:00+00:00\",\n    \"AccountFilePath\": \"data/exchange-account.json\",\n    \"VirtualTrading\": true,\n    \"VirtualTradingFees\": 0.0005,\n    \"VirtualAccountInitialBalance\": 1,\n    \"VirtualAccountFilePath\": \"data/virtual-account.json\"\n  }\n}"
  },
  {
    "path": "Publish/config/web.json",
    "content": "{\n  \"Web\": {\n    \"Enabled\": true,\n    \"DebugMode\": false,\n    \"ReadOnlyMode\": false,\n    \"Port\": 7000,\n    \"SSLEnabled\": false,\n    \"SSLCertPath\": \"data/cert.pfx\",\n    \"SSLCertPassword\": \"certpass\"\n  }\n}"
  },
  {
    "path": "Publish/data/encrypt-keys.bat",
    "content": "dotnet ..\\bin\\IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key\npause"
  },
  {
    "path": "Publish/data/encrypt-keys.sh",
    "content": "#!/bin/bash\n\ndotnet ../bin/IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key"
  },
  {
    "path": "Publish/data/pm2.IntelliTrader.json",
    "content": "{\n  \"apps\": [\n    {\n      \"name\": \"IntelliTrader\",\n      \"cwd\": \".\",\n      \"script\": \"bin/IntelliTrader.dll\",\n      \"node_args\": [],\n      \"log_date_format\": \"YYYY-MM-DD HH:mm Z\",\n      \"exec_interpreter\": \"dotnet\",\n      \"exec_mode\": \"fork\",\n      \"autorestart\": false\n    }\n  ]\n}"
  },
  {
    "path": "Publish.bat",
    "content": "RMDIR /s /q \"Publish/bin\"\ndotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile=\"IntelliTrader/Properties/PublishProfiles/FolderProfile.pubxml\" -o \"../Publish/bin\"\ndotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile=\"IntelliTrader.Web/Properties/PublishProfiles/FolderProfile.pubxml\" -o \"../Publish/bin\"\nECHO \"All done!\"\nPAUSE"
  },
  {
    "path": "Publish.sh",
    "content": "#!/bin/bash\n\ndotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile=\"IntelliTrader/Properties/PublishProfiles/FolderProfile.pubxml\" -o \"../Publish/bin\"\ndotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile=\"IntelliTrader.Web/Properties/PublishProfiles/FolderProfile.pubxml\" -o \"../Publish/bin\""
  },
  {
    "path": "README.md",
    "content": "<img src=\"http://intellitrader.io/img/logo.png\" alt=\"logo\" width=\"40\" height=\"40\"> IntelliTrader\n===========\n\nOverview\n-------------\n\nIntelliTrader is a fully featured, highly configurable trading bot for cryptocurrency exchanges. It is completely free, so please use it at your own risk. Always start by [virtual trading](#virtual-trading) first. Join our [Discord channel](https://discord.gg/rqfpn5a) to get additional help, support and participate in discussions.\n\n##### Main Features\n\n* Cross-platform\n* Highly configurable\n* Easy to use, fast and lightweight\n* Ability to adjust to different market conditions\n* Virtual/paper trading support\n* Backtesting support\n\n##### Additional Resources\n* <img src=\"http://intellitrader.io/img/logo.png\" alt=\"logo\" width=\"20\" height=\"22\"> [Official Website](http://intellitrader.io)\n* <img src=\"http://intellitrader.io/img/discord_icon.png\" alt=\"logo\" width=\"20\" height=\"22\"> [Discord Channel](https://discord.gg/VJZGvrJ)\n* <img src=\"http://intellitrader.io/img/youtube_icon.png\" alt=\"logo\" width=\"20\" height=\"22\"> [Youtube Channel](https://www.youtube.com/channel/UC8Gvv0ArdF9a2CHUPTdqkjg)\n* <img src=\"http://intellitrader.io/img/medium_icon.png\" alt=\"logo\" width=\"20\" height=\"22\"> [Medium](https://medium.com/@intellitrader.io/)\n* <img src=\"http://intellitrader.io/img/tv_icon.png\" alt=\"logo\" width=\"20\" height=\"22\"> [TradingView Scripts](https://www.tradingview.com/scripts/search/intellitrader)\n\nGetting Started\n-------------\n\n#### Prerequisites\n\n###### Windows, Linux & MacOS\nDownload and install .NET Core Runtime 2.1 from [Microsoft](https://www.microsoft.com/net/download/all).\n\n#### Building\n\nYou don't usually need to build the bot yourself, in most cases it's easier to grab the latest published released from [the releases page](https://github.com/jazzonaut/IntelliTrader/releases). But if you are feeling particularly adventurous, here are the steps:\n\n1. Clone the IntelliTrader repository locally\n2. From Git terminal, run 'git submodule update --init --recursive' to pull all the necessary submodules\n3. Run Publish.bat (or Publish.sh if you are on Linux) and it should generate the bin folder in the Publish directory.\n4. That should be it, try running the bot with IntelliTrader to see if it worked.\n\n#### Setting Up\nThe bot should just run with out of the box settings. By default, it is configured for virtual trading, so there is no need to provide any API keys at the start. The only thing you might want to change is the default port for the web interface (7000).\nRefer to [web configuration](#web-configuration) section for information on how to do this.\n\n#### Running\n\nSimply run IntelliTrader to start the bot.  \nRead [Runtime options for Linux and MacOSX](https://github.com/jazzonaut/IntelliTrader/wiki/Runtime-options-for-Linux-and-MacOSX) for additional details when running on these platforms.\n\n#### Supported Exchanges\nCurrenly only Binance Exchange is supported.\n\nConfiguration\n-------------\n\nAll changes to the configuration files will take effect immediately.\n\nConfiguration quick links:\n[Value Types](#value-types), [Core](#core-configuration), [Web](#web-configuration), [Signals](#signals-configuration), [Trading](#trading-configuration), [Rules](#rules-configuration), [Notification](#notification-configuration), [Backtesting](#backtesting-configuration), [Other](#other-configuration)\n\n#### Value Types\n\n|Type|Example|Description|\n|-|:-:|-|\n|Number|0.40|Numeric value|\n|Boolean|true|Boolean value (true/false)|\n|String|Market|String value|\n|Array|[ \"BNBBTC\", \"ETHBTC\" ] |Array of numbers, booleans or strings|\n|Object|{ \"Key\": \"Value\" }|Json object|\n\n#### Core Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|DebugMode|Boolean|true|Enable / disable debug mode|\n|PasswordProtected|Boolean|true|Use password authentication|\n|Password|String|MD5 hash|MD5-encrypted password|\n|InstanceName|String|Main|Must be alphanumeric and should not contain spaces. Used by the web interface & notification service to distinguish between different bot instances (when running multiple)|\n|TimezoneOffset|Number|1|Timezone offset from UTC (in hours), used by stats|\n|HealthCheckEnabled|Boolean|true|Enable / disable health check|\n|HealthCheckInterval|Number|180|Interval to check that all the data is up to date and services are running correctly (in seconds)|\n|HealthCheckSuspendTradingTimeout|Number|900|Suspend trading (disable sells and buys) if any of the health checks did not pass in a specified period of time (in seconds). Set to 0 to continue trading even after health check fails\n|HealthCheckFailuresToRestartServices|Number|3|Restart all services when health check fails for a specified number of times in a row. Set to 0 to disable restart|\n\n#### Web Configuration\n\nRead more about how web interface works in the [web](#web-interface) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable web interface|\n|DebugMode|Boolean|true|Enable / disable debug mode|\n|ReadOnlyMode|Boolean|false|Enable / disable read only mode|\n|Port|Number|7000|Port on which to host the web interface|\n|SSLEnabled|Boolean|false|Enable SSL for web interface|\n|SSLCertPath|String|data/cert.pfx|Path to the SSL certificate|\n|SSLCertPassword|String|certpass|Certificate password|\n\n#### Signals Configuration\n\nRead more about how signals work in the [signals](#signals) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable signals|\n|GlobalRatingSignals|Array|\"TV-5m\",\"TV-15m\",\"TV-1h\"|Signals to calculate the Global Rating from|\n|Definitions|Array|[Signal Definitions](#signal-definitions)|Signal source definitions|\n\n###### Signal Definitions\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Name|String|\"TV-15m\"|Signal name|\n|Receiver|String|\"TradingViewCryptoSignalReceiver\"|Signal receiver name|\n|Configuration|Object|[Signal Receiver Configuration](#signal-receiver-configuration)|Signal receiver configuration|\n\n###### Signal Receiver Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|-|-|\n|PollingInterval|Number|7|How often to check for latest signals (in seconds)|\n|SignalPeriod|Number|15|Signal period (in minutes). Possible values: 5, 15, 60, 240, 1440|\n|VolatilityPeriod|String|\"Day\"|Volatility period. Possible values: Day, Week, Month|\n|RequestUrl|String|\"https://scanner.tradingview.com/crypto/scan\"|Request url|\n|RequestData|String|\"{\\\"filter\\\":[{\\\"left\\\":\\\"exchange\\\",\\\"operation\\\":\\\"equal\\\",\\\"right\\\":\\\"%EXCHANGE%\\\"},{\\\"left\\\":\\\"name\\\",\\\"operation\\\":\\\"match\\\",\\\"right\\\":\\\"%MARKET%\\\"}],\\\"columns\\\":[\\\"name\\\",\\\"close%PERIOD%\\\",\\\"change%PERIOD%\\\",\\\"volume%PERIOD%\\\",\\\"Recommend.All%PERIOD%\\\",\\\"Volatility%VOLATILITY%\\\"],\\\"options\\\":{\\\"lang\\\":\\\"en\\\"},\\\"range\\\":[0,500]}\"|Request data|\n\n#### Trading Configuration\n\nRead more about how trading works in the [trading](#trading) section.\n\n###### General\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable trading|\n|Market|String|\"BTC\"|Market to trade on - BTC, ETH, USDT, etc.|\n|Exchange|String|\"Binance\"|Exchange to trade on|\n|MaxPairs|Number|16|Maximum pairs to trade with|\n|MinCost|Number|0.000999|Ignore pairs with the specified market value or lower (dust)|\n|ExcludedPairs|Array|[ \"BNBBTC\" ]|Pairs excluded from trading|\n|TradePriceType|String|\"Last\"|Price type to use for trading. Available values: Last, Ask, Bid|\n\n###### Buying\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|BuyEnabled|Boolean|true|Enable / disable buying|\n|BuyType|String|\"Market\"|Supported types: Market. Limit is not currently supported|\n|BuyMaxCost|Number|0.0012|Maximum cost when buying a new pair|\n|BuyMultiplier|Number|1|Maximum cost multiplier|\n|BuyMinBalance|Number|0|Minimum account balance to buy new pairs|\n|BuySamePairTimeout|Number|900|Rebuy same pair timeout (in seconds)|\n|BuyTrailing|Number|-0.15|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)|\n|BuyTrailingStopMargin|Number|0.05|Stop trailing and place buy order immediately when margin hits the specified value|\n|BuyTrailingStopAction|String|\"Buy\"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel|\n\n###### Buying DCA\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|BuyDCAEnabled|Boolean|true|Enable / disable buying DCA|\n|BuyDCAMultiplier|Number|1|Current cost multiplier. To double down set to 1|\n|BuyDCAMinBalance|Number|0|Minimum account balance to DCA|\n|BuyDCASamePairTimeout|Number|4200|Rebuy same pair timeout (in seconds)|\n|BuyDCATrailing|Number|-1.50|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)|\n|BuyDCATrailingStopMargin|Number|0.40|Stop trailing and place buy order immediately when margin hits the specified value|\n|BuyDCATrailingStopAction|String|\"Cancel\"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel|\n\n###### Selling\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|SellEnabled|Boolean|true|Enable / disable selling|\n|SellType|String|\"Market\"|Supported types: Market. Limit is not currently supported|\n|SellMargin|Number|2.00|Minimum percentage increase to start trailing|\n|SellTrailing|Number|0.70|Sell trailing percentage (should be a positive number, set to 0 to disable trailing)|\n|SellTrailingStopMargin|Number|1.70|Stop trailing and place sell order immediately when margin hits the specified value|\n|SellTrailingStopAction|String|\"Sell\"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel|\n|SellStopLossEnabled|Boolean|false|Enable / disable stop loss trigger|\n|SellStopLossAfterDCA|Boolean|true|Trigger stop loss only after all DCA levels has been reached|\n|SellStopLossMinAge|Number|3.0|Minimum number of days needed before triggering the stop loss|\n|SellStopLossMargin|Number|-20|Trigger stop loss and immediately place sell order at the specified percentage decrease|\n\n###### Selling DCA\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|SellDCAMargin|Number|1.50|Minimum percentage increase to start trailing|\n|SellDCATrailing|Number|0.50|Sell trailing percentage (set to 0 to disable trailing)\n|SellDCATrailingStopMargin|Number|1.25|Stop trailing and place sell order immediately when margin hits the specified value|\n|SellDCATrailingStopAction|String|\"Sell\"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel|\n|RepeatLastDCALevel|Boolean|fasle|Repeat the last DCA Level indefinitely, essentially making the DCA level number unlimited|\n|DCALevels|Array|[DCA Levels](#dca-levels)|Action to take after hitting the StopMargin. Possible values: Sell, Cancel|\n\n###### DCA Levels\n\nRead more about how DCA works in the [DCA](#dca) section.\n\nDCALevels setting is an array of DCA levels. There is no limit to the number of levels. The only mandatory setting for each level is Margin, which specifies when to trigger the DCA. All other settings are optional and when omitted will use the default values as defined above.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Margin|Number|-1.50|When to trigger the DCA|\n|BuyMultiplier|Number|BuyDCAMultiplier|Current cost multiplier. To double down set to 1 (Optional)|\n|BuySamePairTimeout|Number|BuyDCASamePairTimeout|Rebuy same pair timeout (in seconds) (Optional)|\n|BuyTrailing|Number|BuyDCATrailing|Buy trailing percentage (should be a negative number, set to 0 to disable trailing) (Optional)|\n|BuyTrailingStopMargin|Number|BuyDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)|\n|BuyTrailingStopAction|String|BuyDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Buy, Cancel (Optional)|\n|SellMargin|Number|SellDCAMargin|Minimum percentage increase to start trailing (Optional)|\n|SellTrailing|Number|SellDCATrailing|Sell trailing percentage (set to 0 to disable trailing) (Optional)|\n|SellTrailingStopMargin|Number|SellDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)|\n|SellTrailingStopAction|String|SellDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Sell, Cancel (Optional)|\n\n###### Accounts\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|TradingCheckInterval|Number|1|Tickers check frequency (in seconds)|\n|AccountRefreshInterval|Number|360|Exchange account refresh interval (in seconds)|\n|AccountInitialBalance|Number|0.12|Initial balance on the account, used for stats calculations|\n|AccountInitialBalanceDate|String|\"2018-04-08T00:00:00+00:00\"|Date of the initial balance snapshot|\n|AccountFilePath|String|\"data/exchange-account.json\"|Path to the account file|\n|VirtualTrading|Boolean|true|Enable / disable virtual trading|\n|VirtualTradingFees|Number|0.0005|Trading fees (percentage)|\n|VirtualAccountInitialBalance|Number|0.12|Initial balance on the virtual account, used for stats calculations|\n|VirtualAccountFilePath|String|\"data/virtual-account.json\"|Path to the virtual account file|\n\n#### Rules Configuration\n\nRead more about how rules work in the [rules](#rules) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Modules|Array|[Module](#module)|Rule Modules|\n\n###### Module\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Module|String|\"Module\"|Module name|\n|Configuration|Object|[Module Configuration](#module-configuration)|Module configuration|\n|Entries|Array|[Rules Configuration](#rules-configuration)|Rules configuration|\n\n###### Module Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|ProcessingMode|String|\"AllMatches\"|Available modes: AllMatches (all enabled rules will get processed) or FirstMatch (stop processing at the first rule that is satisfied)|\n|CheckInterval|Number|3|Rules check frequency (in seconds)|\n\n###### Rules Configuration\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enaled|Boolean|true|Enable / disable rule|\n|Name|String|Default|Rule name|\n|Modifiers|Object|[Rule Modifiers](#rule-modifiers)|Rule modifiers|\n|Conditions|Array|[Rule Conditions](#rule-conditions)|Rule conditions|\n|Trailing|Object|[Rule trailing](#rule-trailing)|Rule trailing|\n\n###### Rule Modifiers\n\nAll modifiers (both signal and trading) are optional and can be omitted.  \nSignal modifiers currently only support the *CostMultiplier* (MaxCost multiplier)  \nTrading modifiers support any trading setting that begins with Buy, Sell plus DCALevels and MaxPairs in addition to the following trading-rule specific modifiers:\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|SwapEnabled|Boolean|true|Swap badly performing pairs for better performing ones|\n|SwapSignalRules|Array|[ \"Swap\" ]|Rules used to buy the replacement pair with|\n|SwapTimeout|Number|10800|How long to wait before making a swap (in seconds)|\n|ArbitrageEnabled|Boolean|false|Enable arbitrage for that pair|\n|ArbitrageMarkets|Array|[ \"ETH\" ]|Markets to use for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be used|\n|ArbitrageType|String|\"Reverse\"|Type of arbitrage to use. Available values: Direct, Reverse. When omitted both types will be used|\n|ArbitrageBuyMultiplier|Number|0.99|Percentage of the arbitrage pair to buy|\n|ArbitrageSellMultiplier|Number|0.99|Percentage of the arbitrage pair to sell|\n|ArgbitrageSignalRules|Array|[ \"Arbitrage\" ]|Rules used to arbitrage pairs|\n\n###### Rule Conditions\n\nRule is considered satisfied when all the conditions are met. When trailing is enabled, its conditions must be met first.  \nYou can have multiple sets of conditions within a single rule.  \nIs is not necessary to specify a Signal if none of the conditions are signal-specific.\n\n|Setting|Type|Signal Specific|Description|\n|-|:-:|:-:|-|\n|Signal|String|N/A|Signal with which to calculate signal-specific conditions|\n|MinGlobalRating|Number|No|Minimal global rating calculated from all individual coins ratings. Is a value between -1 and 1. Value -0.2 is considered a bearish market|\n|MaxGlobalRating|Number|No|Maximum global rating calculated from all individual coins ratings. Is a value between -1 and 1. Value +0.2 is considered a bullish market|\n|MinRating|Number|Yes|Minimal rating of a coin within the specified signal's period. Expect a value between -1 and 1. A good place to start is 0.3+|\n|MaxRating|Number|Yes|Maximal ration of a coin within the specified signal's period. Expect a value between -1 and 1. In a normal market do not expect it to go over 0.6|\n|MinRatingChange|Number|Yes|The minimal rate of change of coins rating (percentage). For a reference values let the bot run and then have a look in the dashboard|\n|MaxRatingChange|Number|Yes|The maximal rate of change of coins rating (percentage). For a reference values let the bot run and then have a look in the dashboard|\n|MinPrice|Number|No|Minimum current price of the coin|\n|MaxPrice|Number|No|Maximum current price of the coin|\n|MinPriceChange|Number|Yes|The minimal rate of change of price withing specified period frame (percentage)|\n|MaxPriceChange|Number|Yes|The maximal rate of change of price withing specified period frame (percentage)|\n|MinSpread|Number|No|Minimum difference between current bid and ask price (percentage)|\n|MaxSpread|Number|No|Maximum difference between current bid and ask price (percentage)|\n|MinVolume|Number|Yes|Minimum coin volume within the specified signal's period. Do not expect 24h volume in this category|\n|MaxVolume|Number|Yes|Maximum coin volume within the specified signal's period. Do not expect 24h volume in this category|\n|MinVolumeChange|Number|Yes|The minimal rate of change of volume withing specified period frame (percentage)|\n|MaxVolumeChange|Number|Yes|The maximal rate of change of volume withing specified period frame (percentage)|\n|MinVolatility|Number|Yes|Minimum average volatility of a coin within its own specified timeframe|\n|MaxVolatility|Number|Yes|Maximum average volatility of a coin within its own specified timeframe|\n|MinAge|Number|No|Minimum trading pair's age (in days, e.g. 1.5 is 36 hours)|\n|MaxAge|Number|No|Maximum trading pair's age (in days, e.g. 1.5 is 36 hours)|\n|MinLastBuyAge|Number|No|Minimum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)|\n|MaxLastBuyAge|Number|No|Maximum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)|\n|MinMargin|Number|No|Minimum trading pair's margin|\n|MaxMargin|Number|No|Maximum trading pair's margin|\n|MinMarginChange|Number|No|Minimum trading pair's margin change since last buy|\n|MaxMarginChange|Number|No|Maximum trading pair's margin change since last buy|\n|MinAmount|Number|No|Minimum trading pair's total purchase amount|\n|MaxAmount|Number|No|Maximum trading pair's total purchase amount|\n|MinCost|Number|No|Minimum trading pair's total current cost|\n|MaxCost|Number|No|Maximum trading pair's total current cost|\n|MinDCALevel|Number|No|Minimum trading pair's DCA level|\n|MaxDCALevel|Number|No|Maximum trading pair's DCA level|\n|MinArbitrage|Number|No|Minimum triangular arbitrage value|\n|MaxArbitrage|Number|No|Maximum triangular arbitrage value|\n|ArbitrageMarket|String|No|Market to look for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be considered|\n|ArbitrageType|String|No|Type of arbitrage to look for. Available values: Direct, Reverse. When omitted both types will be considered|\n|Pairs|Array|No|List of pairs to directly apply the rule to|\n|NotPairs|Array|No|List of pairs to not apply the rule to|\n|SignalRules|Array|No|List of signal rules that were used to buy a pair|\n|NotSignalRules|Array|No|List of signal rules that were not used to buy a pair|\n\n###### Rule Trailing\n\nTrailing is optional and is only supported by the Signal Rules.   \nTrailing starts when all the Rule Trailing StartConditions are met.\nTrailing ends when all the conditions of the rule are met, at any point between MinDuration and MaxDuration.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|true|Enable / disable trailing|\n|MinDuration|Number|25|Minimum trail duration (in seconds)|\n|MaxDuration|Number|240|Maximum trail duration (in seconds)|\n|StartConditions|Array|[Rule Conditions](#rule-conditions)|Begin trailing when all the below conditions are met.|\n\n#### Notification Configuration\n\nRead more about how nofitications work in the [notifications](#notifications) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|false|Enable / disable notifications|\n|TelegramEnabled|Boolean|true|Enabled / disable Telegram notifications|\n|TelegramBotToken|String|-|Your Telegram bot's token|\n|TelegramChatId|Number|-|Your Telegram chat id|\n|TelegramAlertsEnabled|Boolean|true|Enable phone alerts with Telegram messages|\n\n#### Backtesting Configuration\n\nRead more about how backtesting works in the [backtesting](#backtesting) section.\n\n|Setting|Type|Default Value|Description|\n|-|:-:|:-:|-|\n|Enabled|Boolean|false|Enable / disable backtesting|\n|Replay|Boolean|false|Enabled / disable snapshots replay|\n|ReplayOutput|Boolean|true|Display replay output in the log|\n|ReplaySpeed|Number|500|Replay speed (1 = SnapshotInterval speed)|\n|ReplayStartIndex|Number|null|Snapshot index to start replay with|\n|ReplayEndIndex|Number|null|Snapshot index to end replay with|\n|DeleteLogs|Boolean|false|Delete all existing logs before running backtesting|\n|DeleteAccountData|Boolean|false|Delete account data before running backtesting|\n|CopyAccountDataPath|String|null|Path to copy existing account data file from for backtesting|\n|TradingSpeedEasing|Number|0|Slow down the trading service while replaying snapshots|\n|TradingRulesSpeedEasing|Number|0|Slow down the trading rules service while replaying snapshots|\n|SignalRulesSpeedEasing|Number|0|Slow down the signal rules service while replaying snapshots|\n|SnapshotsInterval|Number|1|How often to take snapshots (in seconds)|\n|SnapshotsPath|String|data/backtesting|Where to save the new snapshots or to load existing ones when replaying|\n\n#### Other Configuration\n\n###### Exchange\n\nExchange-specific configuration. Don't change the default values unless you know what you're doing.\n\n###### Logging\n\nLogging configuration. Here you can disable logging completely, change the default log file paths, verbosity and format.\n\n###### Paths\n\nPaths to all the configuration files. Do not change unless you know what you're doing.\n\n###### Caching\n\nCurrently not in use.\n\n###### Integration\n\nCurrently not in use.\n\nTrading\n--------------\n\nThere are two ways you can trade with IntelliTrader. Virtual trading is enabled on by default.  \nDO NOT switch to live trading until you are fully confident with the bot and are familiar with the way it does trading.  \nConceptually there is no difference between virtual and live trading, so virtual trading is a very good way to learn the ins and outs, experiment, and try out new settings.\n\n#### Virtual Trading\n\nVirtual trading is enabled by default. You don't need to provide your API Key to virtual trade.  \nTo enable virtual trading, set *VirtualTrading* to true in config/trading.json. \nYou might want to change *VirtualAccountInitialBalance* to reflect your starting balance for testing. \n\n#### Live Trading\n\nTo trade on an exchange, first you need to create an encrypted file that will hold your API keys. \nOpen data/encrypt-keys and change public_key to your API key and private_key to your API secret, then run it.  \nYou should now have the generated keys.bin file in your IntelliTrader directory. This file contains your encrypted API keys and it is only valid for the current user and only on the computer it is created on.  \nImportant: Make sure to remove your keys from data/encrypt-keys file after generating the keys.bin. Now change *VirtualTrading* to false in config/trading.json. \nAlso, to make the profit stats accurate, you need to set *AccountInitialBalance* to your current BTC/ETH balance (depending on the market you use). \nThat's it, you are ready for exchange trading!\n\n#### Trailing\n\nWhen all the buy conditions are true, then the bot is in its final phase where it tries to find the bottom with the help of trailing. The bot is watching the price of a coin closely. The price needs to fall and then rise by at least the percentage specified for trailing in order to make a buy.\n\nExample trailing story for value -1 (percent):\n\n*The coin keeps falling more than 3 percent. It then rises by 0.7 percent. This move is smaller than 1 percent, meaning the bot does nothing and the trailing continues. After another drop the coin jumps 1.5 percent, so the bot will buy because the trailing has exceeded our buy value.*\n\n#### Signals\n\nSignals are used to buy new pairs based on the predefined rules.\n\nRead more about how signal rules work in the [signal rules](#signal-rules) section.\n\n#### Global Rating\n\nAn average value of all the ratings combined (for current market & exchange)\n\n#### DCA\n\nBuy additional position at a lower price than the original purchase price. This brings the average price you've paid the pair down.\n\n#### Stop Loss\n\nSell a pair at a specified negative margin to avoid it dipping even lower.\n\n#### Pair Swapping\n\nSwap badly performing pairs for better performing ones.\nYou would need at least one signal rule and one trading rule to enable this feature. The trading rule will enable swapping feature for the specific pairs (e.g. when the pair is old, has low margin, low rating, etc.) and the signal rule will determine the conditions for which pairs to buy instead of the swapped ones.\n\n#### Arbitrage\n\n*TODO: Add detailed explanation here*\n\n#### Backtesting\n\nBacktesting works by taking snapshots of signals and exchange tickers over a period of time (the longer the period the better) and then replaing them at high speed to the bot. Essentially what this means is that you could snapshot a week's worth of data and then replay it in 10 minutes, saving yourself a lot of time. Snapshots can be reused for as many times as you like and with any settings you wish.\n\nRules\n-------------\n\n#### Signal Rules\n\nSignal Rules are used to buy new pairs based on the specific conditions. This can also optionally have trailing.\nIf enabled then Signal Trailing in IntelliTrader is the first stage of buying where it will trail a coin based on the settings you specify for the time duration's you specify. If a coin matches the trailing conditions it then checks the conditions of the coin and buys the coin if these are met. \nIf trailing isn't enabled it buys based only on the specific conditions set.\n\nAdd \"Action\": \"Swap\" if you want the signal rule to only be used for pair swapping.  \nAdd \"Action\": \"Arbitrage\" if you want the signal rule to only be used for arbitrage.\n\n#### Trading Rules\n\nTrading Rules apply to Pairs you already hold. Trailing is not available for trading rules. Conditions for Trading rules work the same way as in signal rules.\nAvailable modifiers for Trading Rules are any trading.json setting that begins with Buy, Sell or DCALevels.\nThis is also where you would setup your Swap Specific Rules.\n\nWeb Interface\n-------------\n\nTo access the web interface, simply open http://localhost:7000 in your browser (or replace 7000 with the port you have configured).\n\n#### Status Bar\n\nStatus bar contains information about your available balance, current global rating, trailing buys/sells/signals and the current status. Hover over the status icon (ON) to see the latest health check information.\n\n#### Dashboard\n\nThis is the default screen that IntelliTrader starts at. The table shows all the pairs that you currently hold. The data automatically refreshes every 5 seconds.\n\nColumns can be adjusted. You can reorder them, move them around and even remove the ones that you don't need. There are some options for exporting the data: you can copy or export it to a spreadsheet.\n\nYou can click on the table rows to expand them and access additional options. From there you can manually buy, sell or view the current pair's settings. \n\nThe log button will display the last 5 lines from the log file.\n\n|Column|Description|\n|-|-|\n|More|Displayed on low resolution screens and when clicked shows additional data|\n|Pair #1|Pair's name|\n|Pair #2|Pair's name, including the current DCA level|\n|DCA|Current DCA level|\n|Margin|Current profit percentage. If you sold right now, this is what you would make, approximately|\n|Target|Target sell point in profit. Also known as the profit margin|\n|Rating|Pair's current rating based on the signals source, Green means it is currently higher than the purchase rating|\n|Rating Bought|Rating that the pair was purchased at|\n|Age|How long a pair has been held for|\n|Amount|Total amount of the pair you currently hold|\n|Cost|Current value of the pair|\n|Cost Bought|Purchase value of the pair|\n|Price|Current price of the pair on the exchange|\n|Price Bought|Price paid on purchase|\n|Spread|Difference between current bid and ask price|\n|Signal Rule|Signal rule used to buy the pair|\n|Trading Rules|Trading rules currently applied to the pair|\n|Order Dates|The dates of the purchase orders|\n|Order IDs|The order Ids on the exchange|\n\nAlong the bottom of the main area you will find useful information regarding the current pairs, Total Pairs, Average Margin, Average Rating, Average Age, and Total Cost of all pairs together.\n\n#### Market\n\nThis is the current market information page. The table shows all the pairs that are currently available on the exchange along with a variety of data. The data automatically refreshes every 5 seconds.\n\nColumns can be adjust. You can reorder them, move them around and even remove the ones that you don't need. There are some options for exporting the data: you can copy or export it to a spreadsheet.\n\nYou can click on the table rows to expand them and access additional options. From there you can manually buy, buy default (buys at the max cost) or view the current pair's settings. \n\nThe log button will display the last 5 lines from the log file.\n\n|Column|Description|\n|-|-|\n|More|Displayed on low resolution screens and when clicked shows additional data|\n|Name|Pair's name|\n|Rating|Ratings for each signal|\n|% Rating Change|Percentage rating change for each signal|\n|Price|Current pair's price (same for every signal)|\n|% Price Change|Percentage price change for each signal|\n|Spread|Difference between current bid and ask price|\n|Arbitrage|Triangular arbitrage values for every available market|\n|Volume|Volume for each signal|\n|% Volume Change|Percentage volume change for each signal|\n|Volatility|Volatility for each signal|\n|Trading Rules|Currently applied trading rules|\n|Signal Rules|Currently applied signal rules|\n\n#### Stats\n\nHere you can see your total overall profit and current account balance.\nThere is also a breakdown of profits by day, along with other information, like average margin.\n\nYou can click on the number of trades to see information about the orders completed on that particular day.  \n\nYou can also open the Rules Analyzer page to check how each rule is performing.\n\n#### Settings\n\nHere you can temporarily enable or disable a small subset of bot options.  \n\nIf you would like to make permanent changes to settings, you can use the Advanced panel. Saving the changes there will take immediate and permanent effect. In the advanced editor you can switch between different editing modes (Tree, Code, Form, Text, View).\n\nThere are also three options available on that page:\n- Restart Services: this will restarts all the bot's services (core, trading, signals, rules, etc.). Useful when something went wrong and you don't have access to your machine / VPS.\n- Refresh Account: you could use this option if you have made a manual trade on an exchange and would like to update your account immediately. Hoewever, remember that there is a period account refresh (every 6 minutes by default), so this is rarely necessary. \n- Log Out: log out of IntelliTrader\n\n#### Log\n\nDisplays the last 500 lines from the log file.\n\n#### Help\n\nThis page\n\nNotifications\n-------------\n\n#### Telegram\n\nIn the config/notification.json change *Enabled* to true, set *TelegramBotToken* to your bot's token and *TelegramChatId* to your chat id to enable Telegram notifications. Set *TelegramAlertsEnabled* to *false* if you don't want to receive alerts with notifications.  \n\nTalk to @botfather to create a new bot and then talk to @FalconGate_Bot to get your telegram chat id (type /get\\_my\\_id).\n\nHealth Checks\n-------------\n\nHealth check is a periodic test of all the bot's services to make sure that every single one of them is running smoothly and with no interruptions. Trading gets suspended if at least one of the health checks fails until it passes again. If notifications are enabled, you will get a notification for both occasions.\n\nTroubleshooting\n-------------\n\n#### IntelliTrader is not starting or crashing on startup\nPlease made sure that your system meets all the requirements and that you have all the prerequisites. Also check if there are any errors in the log/*-general.log file.\n\n#### IntelliTrader is not trading or is behaving weirdly in general\nAgain, check the log file for any errors, it is very likely that there will be a clue there.\n\nLicense & Disclaimer\n-------------\n\nBy using, or simply downloading IntelliTrader (the Software), you understand and accept the following:\n\n\nLicensing\n\nThe Software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International licence. Full terms of the licence can be found by following this link:\n\nhttps://creativecommons.org/licenses/by-nc-sa/4.0/legalcode\n\nA full copy of the license is also included with the download.\n\n\nLimitation Of Liability\n\nIn no event shall liability be accepted for any indirect, incidental, special, consequential or punitive damages, including, without limitation, loss of profits, funds, data, use, goodwill, or other tangible and intangible losses, resulting from:\n\n\t(i) your access to, or use of, or inability to access or use the Software; \n\t(ii) any content of any third party used with the Software; \n\t(iii) any content obtained from the Software; and \n\t(iv) unauthorized access, use or alteration of your transmissions or content, whether based on warranty, contract, tort (including negligence) or any other legal theory, whether or not we have been informed of the possibility of such damage, and even if a remedy set forth herein is found to have failed of its essential purpose.\n\nWe do not refund losses.\n\n\nDisclaimer\n\nYour use of the Software is at your sole risk. The Software is provided on an “AS IS” and “AS AVAILABLE” basis. The Software is provided without warranties of any kind, whether expressed or implied, including, but not limited to, implied warranties of merchantability, fitness for a particular purpose, non-infringement or course of performance.\n\nNo warranty of any kind is expressed or implied, that:\n\n\ta) the Software will function, be secure or operate on any nominated platform; \n\tb) any errors or defects will be corrected; \n\tc) the Software is free of viruses or other harmful components; or \n\td) the results of using the Software will meet your requirements.\n\n\nIf you do not agree with any of the above, please do not download or use the Software."
  }
]