Repository: jazzonaut/IntelliTrader Branch: master Commit: 146e198c16d2 Files: 247 Total size: 929.6 KB Directory structure: gitextract_1xkwb3jz/ ├── .gitignore ├── .gitmodules ├── Disclaimer.txt ├── IntelliTrader/ │ ├── Help.url │ ├── IntelliTrader.Web.deps.json │ ├── IntelliTrader.csproj │ ├── IntelliTrader.sh │ ├── Program.cs │ ├── Properties/ │ │ └── PublishProfiles/ │ │ └── FolderProfile.pubxml │ ├── config/ │ │ ├── backtesting.json │ │ ├── core.json │ │ ├── exchange.json │ │ ├── logging.json │ │ ├── notification.json │ │ ├── paths.json │ │ ├── rules.json │ │ ├── signals.json │ │ ├── trading.json │ │ └── web.json │ └── data/ │ ├── encrypt-keys.bat │ └── encrypt-keys.sh ├── IntelliTrader.Backtesting/ │ ├── AppModule.cs │ ├── Config/ │ │ └── BacktestingConfig.cs │ ├── IntelliTrader.Backtesting.csproj │ ├── Model/ │ │ ├── SignalData.cs │ │ └── TickerData.cs │ ├── Services/ │ │ ├── BacktestingExchangeService.cs │ │ ├── BacktestingService.cs │ │ └── BacktestingSignalsService.cs │ └── TimedTasks/ │ ├── BacktestingLoadSnapshotsTimedTask.cs │ └── BacktestingSaveSnapshotsTimedTask.cs ├── IntelliTrader.Core/ │ ├── AppModule.cs │ ├── Application.cs │ ├── IntelliTrader.Core.csproj │ ├── Interfaces/ │ │ ├── Configs/ │ │ │ ├── IBacktestingConfig.cs │ │ │ ├── IConfigProvider.cs │ │ │ ├── ICoreConfig.cs │ │ │ ├── ILoggingConfig.cs │ │ │ ├── INotificationConfig.cs │ │ │ ├── IRulesConfig.cs │ │ │ ├── ISignalsConfig.cs │ │ │ ├── ITradingConfig.cs │ │ │ └── IWebConfig.cs │ │ ├── Exchange/ │ │ │ └── ITicker.cs │ │ ├── IHealthCheck.cs │ │ ├── Rules/ │ │ │ ├── IModuleRules.cs │ │ │ ├── IRule.cs │ │ │ ├── IRuleCondition.cs │ │ │ ├── IRuleTrailing.cs │ │ │ ├── ISignalRulesConfig.cs │ │ │ └── RuleProcessingMode.cs │ │ ├── Services/ │ │ │ ├── Base/ │ │ │ │ ├── IConfigurableService.cs │ │ │ │ └── INamedService.cs │ │ │ ├── IBacktestingService.cs │ │ │ ├── ICoreService.cs │ │ │ ├── IExchangeService.cs │ │ │ ├── IHealthCheckService.cs │ │ │ ├── ILoggingService.cs │ │ │ ├── INotificationService.cs │ │ │ ├── IOrderingService.cs │ │ │ ├── IRulesService.cs │ │ │ ├── ISignalsService.cs │ │ │ ├── ITasksService.cs │ │ │ ├── ITradingService.cs │ │ │ └── IWebService.cs │ │ ├── Signals/ │ │ │ ├── ISignal.cs │ │ │ ├── ISignalDefinition.cs │ │ │ └── ISignalTrailingInfo.cs │ │ ├── Tasks/ │ │ │ └── ITimedTask.cs │ │ └── Trading/ │ │ ├── IBuyConfig.cs │ │ ├── IBuyDCAConfig.cs │ │ ├── IOrder.cs │ │ ├── IOrderDetails.cs │ │ ├── IPairConfig.cs │ │ ├── ISellConfig.cs │ │ ├── ISellDCAConfig.cs │ │ ├── ITradeResult.cs │ │ ├── ITradingAccount.cs │ │ └── ITradingPair.cs │ ├── Models/ │ │ ├── Config/ │ │ │ ├── ConfigProvider.cs │ │ │ ├── CoreConfig.cs │ │ │ ├── LoggingConfig.cs │ │ │ └── NotificationConfig.cs │ │ ├── Constants.cs │ │ ├── HealthCheck.cs │ │ ├── Logging/ │ │ │ ├── MemorySink.cs │ │ │ └── MemorySinkExtensions.cs │ │ ├── Tasks/ │ │ │ ├── EqualResolutionTimedTask.cs │ │ │ ├── HighResolutionTimedTask.cs │ │ │ └── LowResolutionTimedTask.cs │ │ ├── Trading/ │ │ │ ├── Arbitrage.cs │ │ │ ├── ArbitrageMarket.cs │ │ │ ├── ArbitrageOptions.cs │ │ │ ├── ArbitrageType.cs │ │ │ ├── BuyOptions.cs │ │ │ ├── BuyTrailingStopAction.cs │ │ │ ├── DCALevel.cs │ │ │ ├── OrderMetadata.cs │ │ │ ├── OrderResult.cs │ │ │ ├── OrderSide.cs │ │ │ ├── OrderType.cs │ │ │ ├── RuleAction.cs │ │ │ ├── SellOptions.cs │ │ │ ├── SellTrailingStopAction.cs │ │ │ ├── SwapOptions.cs │ │ │ ├── TradePriceType.cs │ │ │ └── TradeResult.cs │ │ └── Utils.cs │ ├── Serialization/ │ │ └── DecimalFormatJsonConverter.cs │ ├── Services/ │ │ ├── ConfigurableServiceBase.cs │ │ ├── CoreService.cs │ │ ├── HealthCheckService.cs │ │ ├── LoggingService.cs │ │ ├── NotificationService.cs │ │ └── TasksService.cs │ └── TimedTasks/ │ └── HealthCheckTimedTask.cs ├── IntelliTrader.Exchange.Base/ │ ├── AppModule.cs │ ├── IntelliTrader.Exchange.Base.csproj │ ├── Models/ │ │ ├── BuyOrder.cs │ │ ├── Config/ │ │ │ └── ExchangeConfig.cs │ │ ├── Order.cs │ │ ├── OrderDetails.cs │ │ ├── SellOrder.cs │ │ └── Ticker.cs │ ├── Services/ │ │ └── ExchangeService.cs │ └── TimedTasks/ │ └── TickersMonitorTimedTask.cs ├── IntelliTrader.Exchange.Binance/ │ ├── AppModule.cs │ ├── BinanceExchangeService.cs │ └── IntelliTrader.Exchange.Binance.csproj ├── IntelliTrader.Launcher/ │ ├── IntelliTrader.Launcher.csproj │ ├── Program.cs │ └── Properties/ │ └── AssemblyInfo.cs ├── IntelliTrader.Rules/ │ ├── AppModule.cs │ ├── Config/ │ │ └── RulesConfig.cs │ ├── IntelliTrader.Rules.csproj │ ├── Models/ │ │ ├── ModuleRules.cs │ │ ├── Rule.cs │ │ ├── RuleCondition.cs │ │ └── RuleTrailing.cs │ └── Services/ │ └── RulesService.cs ├── IntelliTrader.Signals.Base/ │ ├── AppModule.cs │ ├── IntelliTrader.Signals.Base.csproj │ ├── Interfaces/ │ │ └── ISignaReceiver.cs │ ├── Models/ │ │ ├── Config/ │ │ │ └── SignalsConfig.cs │ │ ├── Signal.cs │ │ ├── SignalDefinition.cs │ │ ├── SignalRuleModifiers.cs │ │ ├── SignalRulesConfig.cs │ │ └── SignalTrailingInfo.cs │ ├── Services/ │ │ └── SignalsService.cs │ └── TimedTasks/ │ └── SignalRulesTimedTask.cs ├── IntelliTrader.Signals.TradingView/ │ ├── AppModule.cs │ ├── IntelliTrader.Signals.TradingView.csproj │ ├── Models/ │ │ ├── Config/ │ │ │ └── TradingViewCryptoSignalReceiverConfig.cs │ │ └── TradingViewCryptoSignalConverter.cs │ ├── Receivers/ │ │ └── TradingViewCryptoSignalReceiver.cs │ └── TimedTasks/ │ └── TradingViewCryptoSignalPollingTimedTask.cs ├── IntelliTrader.Trading/ │ ├── AppModule.cs │ ├── IntelliTrader.Trading.csproj │ ├── Models/ │ │ ├── Accounts/ │ │ │ ├── ExchangeAccount.cs │ │ │ ├── TradingAccountBase.cs │ │ │ ├── TradingAccountData.cs │ │ │ └── VirtualAccount.cs │ │ ├── BuyTrailingInfo.cs │ │ ├── Config/ │ │ │ └── TradingConfig.cs │ │ ├── PairConfig.cs │ │ ├── SellTrailingInfo.cs │ │ ├── TradingPair.cs │ │ ├── TradingRuleModifiers.cs │ │ ├── TradingRulesConfig.cs │ │ └── TrailingInfo.cs │ ├── Services/ │ │ ├── OrderingService.cs │ │ └── TradingService.cs │ └── TimedTasks/ │ ├── AccountRefreshTimedTask.cs │ ├── TradingRulesTimedTask.cs │ └── TradingTimedTask.cs ├── IntelliTrader.Web/ │ ├── AppModule.cs │ ├── Controllers/ │ │ └── HomeController.cs │ ├── IntelliTrader.Web.csproj │ ├── Misc/ │ │ └── Utils.cs │ ├── Models/ │ │ ├── BaseViewModel.cs │ │ ├── Config/ │ │ │ └── WebConfig.cs │ │ ├── DashboardViewModel.cs │ │ ├── HelpViewModel.cs │ │ ├── LogViewModel.cs │ │ ├── LoginViewModel.cs │ │ ├── MarketViewModel.cs │ │ ├── RulesViewModel.cs │ │ ├── SettingsViewModel.cs │ │ ├── StatsViewModel.cs │ │ └── TradesViewModel.cs │ ├── Properties/ │ │ └── PublishProfiles/ │ │ └── FolderProfile.pubxml │ ├── Services/ │ │ └── WebService.cs │ ├── Startup.cs │ ├── Static/ │ │ ├── Help/ │ │ │ ├── config.json │ │ │ ├── index.md │ │ │ └── navigation.md │ │ ├── Scripts/ │ │ │ ├── Vendor/ │ │ │ │ └── mdwiki.js │ │ │ ├── Views/ │ │ │ │ ├── dashboard.js │ │ │ │ ├── market.js │ │ │ │ ├── rules.js │ │ │ │ ├── settings.js │ │ │ │ ├── stats.js │ │ │ │ └── trades.js │ │ │ └── intellitrader.js │ │ └── Styles/ │ │ ├── Views/ │ │ │ ├── dashboard.css │ │ │ ├── help.css │ │ │ ├── market.css │ │ │ ├── rules.css │ │ │ ├── settings.css │ │ │ ├── stats.css │ │ │ └── trades.css │ │ └── intellitrader.css │ └── Views/ │ ├── Home/ │ │ ├── Dashboard.cshtml │ │ ├── Help.cshtml │ │ ├── Log.cshtml │ │ ├── Login.cshtml │ │ ├── Market.cshtml │ │ ├── Rules.cshtml │ │ ├── Settings.cshtml │ │ ├── Stats.cshtml │ │ └── Trades.cshtml │ ├── Shared/ │ │ └── _Layout.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── IntelliTrader.sln ├── License.txt ├── Publish/ │ ├── Disclaimer.txt │ ├── Help.url │ ├── IntelliTrader.sh │ ├── License.txt │ ├── config/ │ │ ├── backtesting.json │ │ ├── core.json │ │ ├── exchange.json │ │ ├── logging.json │ │ ├── notification.json │ │ ├── paths.json │ │ ├── rules.json │ │ ├── signals.json │ │ ├── trading.json │ │ └── web.json │ └── data/ │ ├── encrypt-keys.bat │ ├── encrypt-keys.sh │ └── pm2.IntelliTrader.json ├── Publish.bat ├── Publish.sh └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ## Custom Tests/ Cache/ Submodules/ keys.bin virtual-account.json exchange-account.json ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted #*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # VScode *vscode* .DS_Store ================================================ FILE: .gitmodules ================================================ [submodule "Submodules/ExchangeSharp"] path = Submodules/ExchangeSharp url = https://github.com/jazzonaut/ExchangeSharp branch = master ================================================ FILE: Disclaimer.txt ================================================ By using, or simply downloading IntelliTrader (the Software), you understand and accept the following: Licensing The 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: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode A full copy of the license is also included with the download. Limitation Of Liability In 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: (i) your access to, or use of, or inability to access or use the Software; (ii) any content of any third party used with the Software; (iii) any content obtained from the Software; and (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. We do not refund losses. Disclaimer Your 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. No warranty of any kind is expressed or implied, that: a) the Software will function, be secure or operate on any nominated platform; b) any errors or defects will be corrected; c) the Software is free of viruses or other harmful components; or d) the results of using the Software will meet your requirements. If you do not agree with any of the above, please do not download or use the Software. ================================================ FILE: IntelliTrader/Help.url ================================================ [InternetShortcut] URL=https://github.com/jazzonaut/IntelliTrader/wiki IconFile=https://assets-cdn.github.com/favicon.ico IconIndex=1 ================================================ FILE: IntelliTrader/IntelliTrader.Web.deps.json ================================================ { "runtimeTarget": { "name": ".NETCoreApp,Version=v2.1", "signature": "53477019973cf406227ea0f01f2112897f901df8" }, "compilationOptions": { "defines": [ "TRACE", "RELEASE", "NETCOREAPP", "NETCOREAPP2_1" ], "languageVersion": "", "platform": "", "allowUnsafe": false, "warningsAsErrors": false, "optimize": true, "keyFile": "", "emitEntryPoint": false, "xmlDoc": false, "debugType": "portable" }, "targets": { ".NETCoreApp,Version=v2.1": { "IntelliTrader.Web/1.0.0": { "dependencies": { "IntelliTrader.Core": "1.0.0", "Microsoft.AspNetCore.Authentication": "2.1.1", "Microsoft.AspNetCore.Authentication.Cookies": "2.1.1", "Microsoft.AspNetCore.Diagnostics": "2.1.1", "Microsoft.AspNetCore.Mvc": "2.1.1", "Microsoft.AspNetCore.Server.Kestrel": "2.1.2", "Microsoft.AspNetCore.StaticFiles": "2.1.1", "Microsoft.NETCore.App": "2.1.0" }, "runtime": { "IntelliTrader.Web.dll": {} }, "compile": { "IntelliTrader.Web.dll": {} } }, "Autofac/4.8.1": { "dependencies": { "NETStandard.Library": "2.0.3", "System.ComponentModel": "4.0.1" }, "runtime": { "lib/netstandard1.1/Autofac.dll": { "assemblyVersion": "4.8.1.0", "fileVersion": "4.8.1.0" } }, "compile": { "lib/netstandard1.1/Autofac.dll": {} } }, "Microsoft.AspNetCore.Antiforgery/2.1.1": { "dependencies": { "Microsoft.AspNetCore.DataProtection": "2.1.1", "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.AspNetCore.WebUtilities": "2.1.1", "Microsoft.Extensions.ObjectPool": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll": {} } }, "Microsoft.AspNetCore.Authentication/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Authentication.Core": "2.1.1", "Microsoft.AspNetCore.DataProtection": "2.1.1", "Microsoft.AspNetCore.Http": "2.1.1", "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1", "Microsoft.Extensions.WebEncoders": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll": {} } }, "Microsoft.AspNetCore.Authentication.Abstractions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Authentication.Cookies/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Authentication": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll": {} } }, "Microsoft.AspNetCore.Authentication.Core/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.1", "Microsoft.AspNetCore.Http": "2.1.1", "Microsoft.AspNetCore.Http.Extensions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll": {} } }, "Microsoft.AspNetCore.Authorization/2.1.1": { "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll": {} } }, "Microsoft.AspNetCore.Authorization.Policy/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.1", "Microsoft.AspNetCore.Authorization": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll": {} } }, "Microsoft.AspNetCore.Connections.Abstractions/2.1.2": { "dependencies": { "Microsoft.AspNetCore.Http.Features": "2.1.1", "System.IO.Pipelines": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.dll": { "assemblyVersion": "2.1.2.0", "fileVersion": "2.1.2.18180" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Cors/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.Extensions.Configuration.Abstractions": "2.1.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll": {} } }, "Microsoft.AspNetCore.Cryptography.Internal/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll": {} } }, "Microsoft.AspNetCore.DataProtection/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Cryptography.Internal": "2.1.1", "Microsoft.AspNetCore.DataProtection.Abstractions": "2.1.1", "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1", "Microsoft.Win32.Registry": "4.5.0", "System.Security.Cryptography.Xml": "4.5.0", "System.Security.Principal.Windows": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll": {} } }, "Microsoft.AspNetCore.DataProtection.Abstractions/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Diagnostics/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Diagnostics.Abstractions": "2.1.1", "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.1", "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.AspNetCore.WebUtilities": "2.1.1", "Microsoft.Extensions.FileProviders.Physical": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1", "System.Diagnostics.DiagnosticSource": "4.5.0", "System.Reflection.Metadata": "1.6.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll": {} } }, "Microsoft.AspNetCore.Diagnostics.Abstractions/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Hosting/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.1", "Microsoft.AspNetCore.Http": "2.1.1", "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.Extensions.Configuration": "2.1.1", "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.1", "Microsoft.Extensions.Configuration.FileExtensions": "2.1.1", "Microsoft.Extensions.DependencyInjection": "2.1.1", "Microsoft.Extensions.FileProviders.Physical": "2.1.1", "Microsoft.Extensions.Hosting.Abstractions": "2.1.1", "Microsoft.Extensions.Logging": "2.1.1", "Microsoft.Extensions.Options": "2.1.1", "System.Diagnostics.DiagnosticSource": "4.5.0", "System.Reflection.Metadata": "1.6.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": {} } }, "Microsoft.AspNetCore.Hosting.Abstractions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.1.1", "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", "Microsoft.Extensions.Hosting.Abstractions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Features": "2.1.1", "Microsoft.Extensions.Configuration.Abstractions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Html.Abstractions/2.1.1": { "dependencies": { "System.Text.Encodings.Web": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Http/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", "Microsoft.AspNetCore.WebUtilities": "2.1.1", "Microsoft.Extensions.ObjectPool": "2.1.1", "Microsoft.Extensions.Options": "2.1.1", "Microsoft.Net.Http.Headers": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": {} } }, "Microsoft.AspNetCore.Http.Abstractions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Features": "2.1.1", "System.Text.Encodings.Web": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Http.Extensions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", "Microsoft.Extensions.FileProviders.Abstractions": "2.1.1", "Microsoft.Net.Http.Headers": "2.1.1", "System.Buffers": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": {} } }, "Microsoft.AspNetCore.Http.Features/2.1.1": { "dependencies": { "Microsoft.Extensions.Primitives": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": {} } }, "Microsoft.AspNetCore.JsonPatch/2.1.1": { "dependencies": { "Microsoft.CSharp": "4.5.0", "Newtonsoft.Json": "11.0.2" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll": {} } }, "Microsoft.AspNetCore.Localization/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.Extensions.Localization.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll": {} } }, "Microsoft.AspNetCore.Mvc/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Mvc.ApiExplorer": "2.1.1", "Microsoft.AspNetCore.Mvc.Cors": "2.1.1", "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.1.1", "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.1.1", "Microsoft.AspNetCore.Mvc.Localization": "2.1.1", "Microsoft.AspNetCore.Mvc.Razor.Extensions": "2.1.1", "Microsoft.AspNetCore.Mvc.RazorPages": "2.1.1", "Microsoft.AspNetCore.Mvc.TagHelpers": "2.1.1", "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.1.1", "Microsoft.AspNetCore.Razor.Design": "2.1.1", "Microsoft.Extensions.Caching.Memory": "2.1.1", "Microsoft.Extensions.DependencyInjection": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll": {} } }, "Microsoft.AspNetCore.Mvc.Abstractions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Routing.Abstractions": "2.1.1", "Microsoft.Net.Http.Headers": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Mvc.ApiExplorer/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Mvc.Core": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll": {} } }, "Microsoft.AspNetCore.Mvc.Core/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Authentication.Core": "2.1.1", "Microsoft.AspNetCore.Authorization.Policy": "2.1.1", "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.1", "Microsoft.AspNetCore.Http": "2.1.1", "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.AspNetCore.Mvc.Abstractions": "2.1.1", "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.1.1", "Microsoft.AspNetCore.Routing": "2.1.1", "Microsoft.Extensions.DependencyInjection": "2.1.1", "Microsoft.Extensions.DependencyModel": "2.1.0", "Microsoft.Extensions.FileProviders.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "System.Diagnostics.DiagnosticSource": "4.5.0", "System.Threading.Tasks.Extensions": "4.5.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll": {} } }, "Microsoft.AspNetCore.Mvc.Cors/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Cors": "2.1.1", "Microsoft.AspNetCore.Mvc.Core": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll": {} } }, "Microsoft.AspNetCore.Mvc.DataAnnotations/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Mvc.Core": "2.1.1", "Microsoft.Extensions.Localization": "2.1.1", "System.ComponentModel.Annotations": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll": {} } }, "Microsoft.AspNetCore.Mvc.Formatters.Json/2.1.1": { "dependencies": { "Microsoft.AspNetCore.JsonPatch": "2.1.1", "Microsoft.AspNetCore.Mvc.Core": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll": {} } }, "Microsoft.AspNetCore.Mvc.Localization/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Localization": "2.1.1", "Microsoft.AspNetCore.Mvc.Razor": "2.1.1", "Microsoft.Extensions.DependencyInjection": "2.1.1", "Microsoft.Extensions.Localization": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll": {} } }, "Microsoft.AspNetCore.Mvc.Razor/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Mvc.Razor.Extensions": "2.1.1", "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.1.1", "Microsoft.AspNetCore.Razor.Runtime": "2.1.1", "Microsoft.CodeAnalysis.CSharp": "2.8.0", "Microsoft.CodeAnalysis.Razor": "2.1.1", "Microsoft.Extensions.Caching.Memory": "2.1.1", "Microsoft.Extensions.FileProviders.Composite": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll": {} } }, "Microsoft.AspNetCore.Mvc.Razor.Extensions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Razor.Language": "2.1.1", "Microsoft.CodeAnalysis.Razor": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll": {} } }, "Microsoft.AspNetCore.Mvc.RazorPages/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Mvc.Razor": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll": {} } }, "Microsoft.AspNetCore.Mvc.TagHelpers/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Mvc.Razor": "2.1.1", "Microsoft.AspNetCore.Razor.Runtime": "2.1.1", "Microsoft.AspNetCore.Routing.Abstractions": "2.1.1", "Microsoft.Extensions.Caching.Memory": "2.1.1", "Microsoft.Extensions.FileSystemGlobbing": "2.1.1", "Microsoft.Extensions.Primitives": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll": {} } }, "Microsoft.AspNetCore.Mvc.ViewFeatures/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Antiforgery": "2.1.1", "Microsoft.AspNetCore.Diagnostics.Abstractions": "2.1.1", "Microsoft.AspNetCore.Html.Abstractions": "2.1.1", "Microsoft.AspNetCore.Mvc.Core": "2.1.1", "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.1.1", "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.1.1", "Microsoft.Extensions.WebEncoders": "2.1.1", "Newtonsoft.Json.Bson": "1.0.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll": {} } }, "Microsoft.AspNetCore.Razor/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Html.Abstractions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll": {} } }, "Microsoft.AspNetCore.Razor.Design/2.1.1": {}, "Microsoft.AspNetCore.Razor.Language/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll": {} } }, "Microsoft.AspNetCore.Razor.Runtime/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Html.Abstractions": "2.1.1", "Microsoft.AspNetCore.Razor": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll": {} } }, "Microsoft.AspNetCore.ResponseCaching.Abstractions/2.1.1": { "dependencies": { "Microsoft.Extensions.Primitives": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Routing/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.AspNetCore.Routing.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.ObjectPool": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Routing.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Routing.dll": {} } }, "Microsoft.AspNetCore.Routing.Abstractions/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Server.Kestrel/2.1.2": { "dependencies": { "Microsoft.AspNetCore.Hosting": "2.1.1", "Microsoft.AspNetCore.Server.Kestrel.Core": "2.1.2", "Microsoft.AspNetCore.Server.Kestrel.Https": "2.1.2", "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets": "2.1.2" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll": { "assemblyVersion": "2.1.2.0", "fileVersion": "2.1.2.18180" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll": {} } }, "Microsoft.AspNetCore.Server.Kestrel.Core/2.1.2": { "dependencies": { "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.1", "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.1.2", "Microsoft.AspNetCore.WebUtilities": "2.1.1", "Microsoft.Extensions.Configuration.Binder": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1", "Microsoft.Net.Http.Headers": "2.1.1", "System.Memory": "4.5.1", "System.Numerics.Vectors": "4.5.0", "System.Runtime.CompilerServices.Unsafe": "4.5.1", "System.Security.Cryptography.Cng": "4.5.0", "System.Threading.Tasks.Extensions": "4.5.1" }, "runtime": { "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll": { "assemblyVersion": "2.1.2.0", "fileVersion": "2.1.2.18180" } }, "compile": { "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll": {} } }, "Microsoft.AspNetCore.Server.Kestrel.Https/2.1.2": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", "Microsoft.AspNetCore.Server.Kestrel.Core": "2.1.2" }, "runtime": { "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll": { "assemblyVersion": "2.1.2.0", "fileVersion": "2.1.2.18180" } }, "compile": { "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll": {} } }, "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.1.2": { "dependencies": { "Microsoft.AspNetCore.Connections.Abstractions": "2.1.2" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll": { "assemblyVersion": "2.1.2.0", "fileVersion": "2.1.2.18180" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll": {} } }, "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.1.2": { "dependencies": { "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.1", "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.1.2", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll": { "assemblyVersion": "2.1.2.0", "fileVersion": "2.1.2.18180" } }, "compile": { "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll": {} } }, "Microsoft.AspNetCore.StaticFiles/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.1", "Microsoft.AspNetCore.Http.Extensions": "2.1.1", "Microsoft.Extensions.FileProviders.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.WebEncoders": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll": {} } }, "Microsoft.AspNetCore.WebUtilities/2.1.1": { "dependencies": { "Microsoft.Net.Http.Headers": "2.1.1", "System.Text.Encodings.Web": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": {} } }, "Microsoft.CodeAnalysis.Analyzers/1.1.0": {}, "Microsoft.CodeAnalysis.Common/2.8.0": { "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "1.1.0", "System.AppContext": "4.3.0", "System.Collections": "4.3.0", "System.Collections.Concurrent": "4.3.0", "System.Collections.Immutable": "1.3.1", "System.Console": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Diagnostics.FileVersionInfo": "4.3.0", "System.Diagnostics.StackTrace": "4.3.0", "System.Diagnostics.Tools": "4.3.0", "System.Dynamic.Runtime": "4.3.0", "System.Globalization": "4.3.0", "System.IO.Compression": "4.3.0", "System.IO.FileSystem": "4.3.0", "System.IO.FileSystem.Primitives": "4.3.0", "System.Linq": "4.3.0", "System.Linq.Expressions": "4.3.0", "System.Reflection": "4.3.0", "System.Reflection.Metadata": "1.6.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Runtime.Numerics": "4.3.0", "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Cryptography.Encoding": "4.3.0", "System.Security.Cryptography.X509Certificates": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Text.Encoding.CodePages": "4.3.0", "System.Text.Encoding.Extensions": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Tasks": "4.3.0", "System.Threading.Tasks.Parallel": "4.3.0", "System.Threading.Thread": "4.3.0", "System.ValueTuple": "4.3.0", "System.Xml.ReaderWriter": "4.3.0", "System.Xml.XDocument": "4.3.0", "System.Xml.XPath.XDocument": "4.3.0", "System.Xml.XmlDocument": "4.3.0" }, "runtime": { "lib/netstandard1.3/Microsoft.CodeAnalysis.dll": { "assemblyVersion": "2.8.0.0", "fileVersion": "2.8.0.62830" } }, "compile": { "lib/netstandard1.3/Microsoft.CodeAnalysis.dll": {} } }, "Microsoft.CodeAnalysis.CSharp/2.8.0": { "dependencies": { "Microsoft.CodeAnalysis.Common": "2.8.0" }, "runtime": { "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll": { "assemblyVersion": "2.8.0.0", "fileVersion": "2.8.0.62830" } }, "compile": { "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll": {} } }, "Microsoft.CodeAnalysis.Razor/2.1.1": { "dependencies": { "Microsoft.AspNetCore.Razor.Language": "2.1.1", "Microsoft.CodeAnalysis.CSharp": "2.8.0", "Microsoft.CodeAnalysis.Common": "2.8.0" }, "runtime": { "lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll": {} } }, "Microsoft.CSharp/4.5.0": {}, "Microsoft.DotNet.PlatformAbstractions/2.1.0": { "dependencies": { "System.AppContext": "4.3.0", "System.Collections": "4.3.0", "System.IO": "4.3.0", "System.IO.FileSystem": "4.3.0", "System.Reflection.TypeExtensions": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" }, "runtime": { "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": { "assemblyVersion": "2.1.0.0", "fileVersion": "2.1.0.0" } }, "compile": { "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": {} } }, "Microsoft.Extensions.Caching.Abstractions/2.1.1": { "dependencies": { "Microsoft.Extensions.Primitives": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {} } }, "Microsoft.Extensions.Caching.Memory/2.1.1": { "dependencies": { "Microsoft.Extensions.Caching.Abstractions": "2.1.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {} } }, "Microsoft.Extensions.Configuration/2.1.1": { "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": {} } }, "Microsoft.Extensions.Configuration.Abstractions/2.1.1": { "dependencies": { "Microsoft.Extensions.Primitives": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {} } }, "Microsoft.Extensions.Configuration.Binder/2.1.1": { "dependencies": { "Microsoft.Extensions.Configuration": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": {} } }, "Microsoft.Extensions.Configuration.EnvironmentVariables/2.1.1": { "dependencies": { "Microsoft.Extensions.Configuration": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": {} } }, "Microsoft.Extensions.Configuration.FileExtensions/2.1.1": { "dependencies": { "Microsoft.Extensions.Configuration": "2.1.1", "Microsoft.Extensions.FileProviders.Physical": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {} } }, "Microsoft.Extensions.Configuration.Json/2.1.1": { "dependencies": { "Microsoft.Extensions.Configuration": "2.1.1", "Microsoft.Extensions.Configuration.FileExtensions": "2.1.1", "Newtonsoft.Json": "11.0.2" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll": {} } }, "Microsoft.Extensions.DependencyInjection/2.1.1": { "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1" }, "runtime": { "lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.dll": {} } }, "Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {} } }, "Microsoft.Extensions.DependencyModel/2.1.0": { "dependencies": { "Microsoft.DotNet.PlatformAbstractions": "2.1.0", "Newtonsoft.Json": "11.0.2", "System.Diagnostics.Debug": "4.3.0", "System.Dynamic.Runtime": "4.3.0", "System.Linq": "4.3.0" }, "runtime": { "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll": { "assemblyVersion": "2.1.0.0", "fileVersion": "2.1.0.0" } }, "compile": { "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll": {} } }, "Microsoft.Extensions.FileProviders.Abstractions/2.1.1": { "dependencies": { "Microsoft.Extensions.Primitives": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {} } }, "Microsoft.Extensions.FileProviders.Composite/2.1.1": { "dependencies": { "Microsoft.Extensions.FileProviders.Abstractions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll": {} } }, "Microsoft.Extensions.FileProviders.Physical/2.1.1": { "dependencies": { "Microsoft.Extensions.FileProviders.Abstractions": "2.1.1", "Microsoft.Extensions.FileSystemGlobbing": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": {} } }, "Microsoft.Extensions.FileSystemGlobbing/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": {} } }, "Microsoft.Extensions.Hosting.Abstractions/2.1.1": { "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "2.1.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.FileProviders.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": {} } }, "Microsoft.Extensions.Localization/2.1.1": { "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.Localization.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Localization.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Localization.dll": {} } }, "Microsoft.Extensions.Localization.Abstractions/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll": {} } }, "Microsoft.Extensions.Logging/2.1.1": { "dependencies": { "Microsoft.Extensions.Configuration.Binder": "2.1.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.Logging.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Logging.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Logging.dll": {} } }, "Microsoft.Extensions.Logging.Abstractions/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {} } }, "Microsoft.Extensions.ObjectPool/2.1.1": { "runtime": { "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": {} } }, "Microsoft.Extensions.Options/2.1.1": { "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.Primitives": "2.1.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Options.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Options.dll": {} } }, "Microsoft.Extensions.Primitives/2.1.1": { "dependencies": { "System.Memory": "4.5.1", "System.Runtime.CompilerServices.Unsafe": "4.5.1" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll": {} } }, "Microsoft.Extensions.WebEncoders/2.1.1": { "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", "Microsoft.Extensions.Options": "2.1.1", "System.Text.Encodings.Web": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll": {} } }, "Microsoft.Net.Http.Headers/2.1.1": { "dependencies": { "Microsoft.Extensions.Primitives": "2.1.1", "System.Buffers": "4.5.0" }, "runtime": { "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": { "assemblyVersion": "2.1.1.0", "fileVersion": "2.1.1.18157" } }, "compile": { "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": {} } }, "Microsoft.Win32.Registry/4.5.0": { "dependencies": { "System.Security.AccessControl": "4.5.0", "System.Security.Principal.Windows": "4.5.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } }, "compile": { "ref/netstandard2.0/Microsoft.Win32.Registry.dll": {} } }, "Newtonsoft.Json/11.0.2": { "runtime": { "lib/netstandard2.0/Newtonsoft.Json.dll": { "assemblyVersion": "11.0.0.0", "fileVersion": "11.0.2.21924" } }, "compile": { "lib/netstandard2.0/Newtonsoft.Json.dll": {} } }, "Newtonsoft.Json.Bson/1.0.1": { "dependencies": { "NETStandard.Library": "2.0.3", "Newtonsoft.Json": "11.0.2" }, "runtime": { "lib/netstandard1.3/Newtonsoft.Json.Bson.dll": { "assemblyVersion": "1.0.0.0", "fileVersion": "1.0.1.20722" } }, "compile": { "lib/netstandard1.3/Newtonsoft.Json.Bson.dll": {} } }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/debian.8-x64/native/_._": { "rid": "debian.8-x64", "assetType": "native" } } }, "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/fedora.23-x64/native/_._": { "rid": "fedora.23-x64", "assetType": "native" } } }, "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/fedora.24-x64/native/_._": { "rid": "fedora.24-x64", "assetType": "native" } } }, "runtime.native.System/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0" } }, "runtime.native.System.IO.Compression/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0" } }, "runtime.native.System.Net.Http/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0" } }, "runtime.native.System.Security.Cryptography.Apple/4.3.0": { "dependencies": { "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" } }, "runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "dependencies": { "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" } }, "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/opensuse.13.2-x64/native/_._": { "rid": "opensuse.13.2-x64", "assetType": "native" } } }, "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/opensuse.42.1-x64/native/_._": { "rid": "opensuse.42.1-x64", "assetType": "native" } } }, "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { "runtimeTargets": { "runtime/osx.10.10-x64/native/_._": { "rid": "osx.10.10-x64", "assetType": "native" } } }, "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/osx.10.10-x64/native/_._": { "rid": "osx.10.10-x64", "assetType": "native" } } }, "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/rhel.7-x64/native/_._": { "rid": "rhel.7-x64", "assetType": "native" } } }, "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/ubuntu.14.04-x64/native/_._": { "rid": "ubuntu.14.04-x64", "assetType": "native" } } }, "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/ubuntu.16.04-x64/native/_._": { "rid": "ubuntu.16.04-x64", "assetType": "native" } } }, "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "runtimeTargets": { "runtime/ubuntu.16.10-x64/native/_._": { "rid": "ubuntu.16.10-x64", "assetType": "native" } } }, "Serilog/2.7.1": { "dependencies": { "NETStandard.Library": "2.0.3", "System.Collections.NonGeneric": "4.3.0" }, "runtime": { "lib/netstandard1.3/Serilog.dll": { "assemblyVersion": "2.0.0.0", "fileVersion": "2.7.1.0" } }, "compile": { "lib/netstandard1.3/Serilog.dll": {} } }, "Serilog.Enrichers.Environment/2.1.2": { "dependencies": { "Serilog": "2.7.1" }, "runtime": { "lib/netstandard1.3/Serilog.Enrichers.Environment.dll": { "assemblyVersion": "2.0.0.0", "fileVersion": "2.1.2.0" } }, "compile": { "lib/netstandard1.3/Serilog.Enrichers.Environment.dll": {} } }, "Serilog.Filters.Expressions/2.0.0": { "dependencies": { "NETStandard.Library": "2.0.3", "Serilog": "2.7.1", "Superpower": "2.0.0" }, "runtime": { "lib/netstandard1.5/Serilog.Filters.Expressions.dll": { "assemblyVersion": "2.0.0.0", "fileVersion": "2.0.0.0" } }, "compile": { "lib/netstandard1.5/Serilog.Filters.Expressions.dll": {} } }, "Serilog.Settings.Configuration/2.6.1": { "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "2.1.1", "Microsoft.Extensions.DependencyModel": "2.1.0", "Serilog": "2.7.1" }, "runtime": { "lib/netstandard1.6/Serilog.Settings.Configuration.dll": { "assemblyVersion": "2.6.1.0", "fileVersion": "2.6.1.0" } }, "compile": { "lib/netstandard1.6/Serilog.Settings.Configuration.dll": {} } }, "Serilog.Sinks.Console/3.1.1": { "dependencies": { "Serilog": "2.7.1", "System.Console": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" }, "runtime": { "lib/netcoreapp1.1/Serilog.Sinks.Console.dll": { "assemblyVersion": "3.1.1.0", "fileVersion": "3.1.1.0" } }, "compile": { "lib/netcoreapp1.1/Serilog.Sinks.Console.dll": {} } }, "Serilog.Sinks.File/3.2.0": { "dependencies": { "Serilog": "2.7.1", "System.IO": "4.3.0", "System.IO.FileSystem": "4.3.0", "System.IO.FileSystem.Primitives": "4.3.0", "System.Text.Encoding.Extensions": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Timer": "4.0.1" }, "runtime": { "lib/netstandard1.3/Serilog.Sinks.File.dll": { "assemblyVersion": "2.0.0.0", "fileVersion": "3.2.0.0" } }, "compile": { "lib/netstandard1.3/Serilog.Sinks.File.dll": {} } }, "Serilog.Sinks.RollingFile/3.3.0": { "dependencies": { "Serilog.Sinks.File": "3.2.0", "System.IO": "4.3.0", "System.IO.FileSystem.Primitives": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Text.Encoding.Extensions": "4.3.0" }, "runtime": { "lib/netstandard1.3/Serilog.Sinks.RollingFile.dll": { "assemblyVersion": "2.0.0.0", "fileVersion": "3.3.0.0" } }, "compile": { "lib/netstandard1.3/Serilog.Sinks.RollingFile.dll": {} } }, "Superpower/2.0.0": { "dependencies": { "NETStandard.Library": "2.0.3" }, "runtime": { "lib/netstandard1.0/Superpower.dll": { "assemblyVersion": "1.0.0.0", "fileVersion": "2.0.0.0" } }, "compile": { "lib/netstandard1.0/Superpower.dll": {} } }, "System.AppContext/4.3.0": { "dependencies": { "System.Runtime": "4.3.0" } }, "System.Buffers/4.5.0": {}, "System.Collections/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Collections.Concurrent/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Diagnostics.Tracing": "4.3.0", "System.Globalization": "4.3.0", "System.Reflection": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Tasks": "4.3.0" } }, "System.Collections.Immutable/1.3.1": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.Linq": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0" } }, "System.Collections.NonGeneric/4.3.0": { "dependencies": { "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0" } }, "System.ComponentModel/4.0.1": { "dependencies": { "System.Runtime": "4.3.0" } }, "System.ComponentModel.Annotations/4.5.0": {}, "System.Console/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.IO": "4.3.0", "System.Runtime": "4.3.0", "System.Text.Encoding": "4.3.0" } }, "System.Diagnostics.Debug/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Diagnostics.DiagnosticSource/4.5.0": {}, "System.Diagnostics.FileVersionInfo/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.IO.FileSystem": "4.3.0", "System.IO.FileSystem.Primitives": "4.3.0", "System.Reflection.Metadata": "1.6.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.InteropServices": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Diagnostics.StackTrace/4.3.0": { "dependencies": { "System.IO.FileSystem": "4.3.0", "System.Reflection": "4.3.0", "System.Reflection.Metadata": "1.6.0", "System.Runtime": "4.3.0" } }, "System.Diagnostics.Tools/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Diagnostics.Tracing/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Dynamic.Runtime/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Linq": "4.3.0", "System.Linq.Expressions": "4.3.0", "System.ObjectModel": "4.3.0", "System.Reflection": "4.3.0", "System.Reflection.Emit": "4.3.0", "System.Reflection.Emit.ILGeneration": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Reflection.TypeExtensions": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0" } }, "System.Globalization/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Globalization.Calendars/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Globalization": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Globalization.Extensions/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Globalization": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.InteropServices": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.IO/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading.Tasks": "4.3.0" } }, "System.IO.Compression/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Buffers": "4.5.0", "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.IO": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Tasks": "4.3.0", "runtime.native.System": "4.3.0", "runtime.native.System.IO.Compression": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.IO.FileSystem/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.IO": "4.3.0", "System.IO.FileSystem.Primitives": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading.Tasks": "4.3.0" } }, "System.IO.FileSystem.Primitives/4.3.0": { "dependencies": { "System.Runtime": "4.3.0" } }, "System.IO.Pipelines/4.5.0": { "runtime": { "lib/netcoreapp2.1/System.IO.Pipelines.dll": { "assemblyVersion": "4.0.0.0", "fileVersion": "4.6.26515.6" } }, "compile": { "ref/netstandard1.3/System.IO.Pipelines.dll": {} } }, "System.Linq/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0" } }, "System.Linq.Expressions/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.Linq": "4.3.0", "System.ObjectModel": "4.3.0", "System.Reflection": "4.3.0", "System.Reflection.Emit": "4.3.0", "System.Reflection.Emit.ILGeneration": "4.3.0", "System.Reflection.Emit.Lightweight": "4.3.0", "System.Reflection.Extensions": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Reflection.TypeExtensions": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0" } }, "System.Memory/4.5.1": {}, "System.Net.Http/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Diagnostics.DiagnosticSource": "4.5.0", "System.Diagnostics.Tracing": "4.3.0", "System.Globalization": "4.3.0", "System.Globalization.Extensions": "4.3.0", "System.IO": "4.3.0", "System.IO.FileSystem": "4.3.0", "System.Net.Primitives": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Cryptography.Encoding": "4.3.0", "System.Security.Cryptography.OpenSsl": "4.3.0", "System.Security.Cryptography.Primitives": "4.3.0", "System.Security.Cryptography.X509Certificates": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Tasks": "4.3.0", "runtime.native.System": "4.3.0", "runtime.native.System.Net.Http": "4.3.0", "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Net.Primitives/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0", "System.Runtime.Handles": "4.3.0" } }, "System.Net.Requests/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Diagnostics.Tracing": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.Net.Http": "4.3.0", "System.Net.Primitives": "4.3.0", "System.Net.WebHeaderCollection": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Tasks": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Net.WebHeaderCollection/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0" } }, "System.Numerics.Vectors/4.5.0": {}, "System.ObjectModel/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Threading": "4.3.0" } }, "System.Reflection/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.IO": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Reflection.Emit/4.3.0": { "dependencies": { "System.IO": "4.3.0", "System.Reflection": "4.3.0", "System.Reflection.Emit.ILGeneration": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Reflection.Emit.ILGeneration/4.3.0": { "dependencies": { "System.Reflection": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Reflection.Emit.Lightweight/4.3.0": { "dependencies": { "System.Reflection": "4.3.0", "System.Reflection.Emit.ILGeneration": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Reflection.Extensions/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Reflection": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Reflection.Metadata/1.6.0": {}, "System.Reflection.Primitives/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Reflection.TypeExtensions/4.3.0": { "dependencies": { "System.Reflection": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Resources.ResourceManager/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Globalization": "4.3.0", "System.Reflection": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Runtime/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0" } }, "System.Runtime.CompilerServices.Unsafe/4.5.1": { "runtime": { "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll": { "assemblyVersion": "4.0.4.0", "fileVersion": "0.0.0.0" } }, "compile": { "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} } }, "System.Runtime.Extensions/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Runtime.Handles/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Runtime.InteropServices/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Reflection": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Handles": "4.3.0" } }, "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { "dependencies": { "System.Reflection": "4.3.0", "System.Reflection.Extensions": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Threading": "4.3.0", "runtime.native.System": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Runtime.Numerics/4.3.0": { "dependencies": { "System.Globalization": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0" } }, "System.Security.AccessControl/4.5.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Security.Principal.Windows": "4.5.0" }, "runtimeTargets": { "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } }, "compile": { "ref/netstandard2.0/System.Security.AccessControl.dll": {} } }, "System.Security.Cryptography.Algorithms/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Collections": "4.3.0", "System.IO": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Runtime.Numerics": "4.3.0", "System.Security.Cryptography.Encoding": "4.3.0", "System.Security.Cryptography.Primitives": "4.3.0", "System.Text.Encoding": "4.3.0", "runtime.native.System.Security.Cryptography.Apple": "4.3.0", "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" }, "runtimeTargets": { "runtime/osx/lib/_._": { "rid": "osx", "assetType": "runtime" }, "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Security.Cryptography.Cng/4.5.0": { "runtimeTargets": { "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } }, "compile": { "ref/netcoreapp2.1/System.Security.Cryptography.Cng.dll": {} } }, "System.Security.Cryptography.Csp/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.IO": "4.3.0", "System.Reflection": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Cryptography.Encoding": "4.3.0", "System.Security.Cryptography.Primitives": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Security.Cryptography.Encoding/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Collections": "4.3.0", "System.Collections.Concurrent": "4.3.0", "System.Linq": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Security.Cryptography.Primitives": "4.3.0", "System.Text.Encoding": "4.3.0", "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Security.Cryptography.OpenSsl/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.IO": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Runtime.Numerics": "4.3.0", "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Cryptography.Encoding": "4.3.0", "System.Security.Cryptography.Primitives": "4.3.0", "System.Text.Encoding": "4.3.0", "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" } } }, "System.Security.Cryptography.Pkcs/4.5.0": { "dependencies": { "System.Security.Cryptography.Cng": "4.5.0" }, "runtime": { "lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll": { "assemblyVersion": "4.0.3.0", "fileVersion": "4.6.26515.6" } }, "runtimeTargets": { "runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll": { "rid": "win", "assetType": "runtime", "assemblyVersion": "4.0.3.0", "fileVersion": "4.6.26515.6" } } }, "System.Security.Cryptography.Primitives/4.3.0": { "dependencies": { "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Tasks": "4.3.0" } }, "System.Security.Cryptography.X509Certificates/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.Globalization.Calendars": "4.3.0", "System.IO": "4.3.0", "System.IO.FileSystem": "4.3.0", "System.IO.FileSystem.Primitives": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Runtime.Numerics": "4.3.0", "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Cryptography.Cng": "4.5.0", "System.Security.Cryptography.Csp": "4.3.0", "System.Security.Cryptography.Encoding": "4.3.0", "System.Security.Cryptography.OpenSsl": "4.3.0", "System.Security.Cryptography.Primitives": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading": "4.3.0", "runtime.native.System": "4.3.0", "runtime.native.System.Net.Http": "4.3.0", "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } } }, "System.Security.Cryptography.Xml/4.5.0": { "dependencies": { "System.Security.Cryptography.Pkcs": "4.5.0", "System.Security.Permissions": "4.5.0" }, "runtime": { "lib/netstandard2.0/System.Security.Cryptography.Xml.dll": { "assemblyVersion": "4.0.1.0", "fileVersion": "4.6.26515.6" } }, "compile": { "ref/netstandard2.0/System.Security.Cryptography.Xml.dll": {} } }, "System.Security.Permissions/4.5.0": { "dependencies": { "System.Security.AccessControl": "4.5.0" }, "runtime": { "lib/netstandard2.0/System.Security.Permissions.dll": { "assemblyVersion": "4.0.1.0", "fileVersion": "4.6.26515.6" } }, "compile": { "ref/netstandard2.0/System.Security.Permissions.dll": {} } }, "System.Security.Principal.Windows/4.5.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0" }, "runtimeTargets": { "runtime/unix/lib/_._": { "rid": "unix", "assetType": "runtime" }, "runtime/win/lib/_._": { "rid": "win", "assetType": "runtime" } }, "compile": { "ref/netstandard2.0/System.Security.Principal.Windows.dll": {} } }, "System.Text.Encoding/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Text.Encoding.CodePages/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "System.Collections": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.Reflection": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.Handles": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading": "4.3.0" }, "runtimeTargets": { "runtimes/unix/lib/netstandard1.3/System.Text.Encoding.CodePages.dll": { "rid": "unix", "assetType": "runtime", "assemblyVersion": "4.0.2.0", "fileVersion": "4.6.24705.1" }, "runtimes/win/lib/netstandard1.3/System.Text.Encoding.CodePages.dll": { "rid": "win", "assetType": "runtime", "assemblyVersion": "4.0.2.0", "fileVersion": "4.6.24705.1" } } }, "System.Text.Encoding.Extensions/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0", "System.Text.Encoding": "4.3.0" } }, "System.Text.Encodings.Web/4.5.0": { "runtime": { "lib/netstandard2.0/System.Text.Encodings.Web.dll": { "assemblyVersion": "4.0.3.0", "fileVersion": "4.6.26515.6" } }, "compile": { "lib/netstandard2.0/System.Text.Encodings.Web.dll": {} } }, "System.Text.RegularExpressions/4.3.0": { "dependencies": { "System.Runtime": "4.3.0" } }, "System.Threading/4.3.0": { "dependencies": { "System.Runtime": "4.3.0", "System.Threading.Tasks": "4.3.0" } }, "System.Threading.Tasks/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.Threading.Tasks.Extensions/4.5.1": {}, "System.Threading.Tasks.Parallel/4.3.0": { "dependencies": { "System.Collections.Concurrent": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Diagnostics.Tracing": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0", "System.Threading.Tasks": "4.3.0" } }, "System.Threading.Thread/4.3.0": { "dependencies": { "System.Runtime": "4.3.0" } }, "System.Threading.Timer/4.0.1": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "System.Runtime": "4.3.0" } }, "System.ValueTuple/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0" } }, "System.Xml.ReaderWriter/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.IO.FileSystem": "4.3.0", "System.IO.FileSystem.Primitives": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Runtime.InteropServices": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Text.Encoding.Extensions": "4.3.0", "System.Text.RegularExpressions": "4.3.0", "System.Threading.Tasks": "4.3.0", "System.Threading.Tasks.Extensions": "4.5.1" } }, "System.Xml.XDocument/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Diagnostics.Tools": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.Reflection": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading": "4.3.0", "System.Xml.ReaderWriter": "4.3.0" } }, "System.Xml.XmlDocument/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Text.Encoding": "4.3.0", "System.Threading": "4.3.0", "System.Xml.ReaderWriter": "4.3.0" } }, "System.Xml.XPath/4.3.0": { "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", "System.IO": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0", "System.Xml.ReaderWriter": "4.3.0" } }, "System.Xml.XPath.XDocument/4.3.0": { "dependencies": { "System.Diagnostics.Debug": "4.3.0", "System.Linq": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", "System.Threading": "4.3.0", "System.Xml.ReaderWriter": "4.3.0", "System.Xml.XDocument": "4.3.0", "System.Xml.XPath": "4.3.0" } }, "Telegram.Bot/14.6.0": { "dependencies": { "NETStandard.Library": "2.0.3", "Newtonsoft.Json": "11.0.2", "System.Net.Requests": "4.3.0" }, "runtime": { "lib/netstandard1.1/Telegram.Bot.dll": { "assemblyVersion": "14.6.0.0", "fileVersion": "14.6.0.0" } }, "compile": { "lib/netstandard1.1/Telegram.Bot.dll": {} } }, "IntelliTrader.Core/1.0.0": { "dependencies": { "Autofac": "4.8.1", "Microsoft.Extensions.Configuration": "2.1.1", "Microsoft.Extensions.Configuration.Binder": "2.1.1", "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.1", "Microsoft.Extensions.Configuration.Json": "2.1.1", "Serilog": "2.7.1", "Serilog.Enrichers.Environment": "2.1.2", "Serilog.Filters.Expressions": "2.0.0", "Serilog.Settings.Configuration": "2.6.1", "Serilog.Sinks.Console": "3.1.1", "Serilog.Sinks.RollingFile": "3.3.0", "Telegram.Bot": "14.6.0" }, "runtime": { "IntelliTrader.Core.dll": {} }, "compile": { "IntelliTrader.Core.dll": {} } }, "Microsoft.NETCore.App/2.1.0": { "dependencies": { "Microsoft.NETCore.DotNetHostPolicy": "2.1.0", "Microsoft.NETCore.Platforms": "2.1.0", "Microsoft.NETCore.Targets": "2.1.0", "NETStandard.Library": "2.0.3" }, "compile": { "ref/netcoreapp2.1/Microsoft.CSharp.dll": {}, "ref/netcoreapp2.1/Microsoft.VisualBasic.dll": {}, "ref/netcoreapp2.1/Microsoft.Win32.Primitives.dll": {}, "ref/netcoreapp2.1/System.AppContext.dll": {}, "ref/netcoreapp2.1/System.Buffers.dll": {}, "ref/netcoreapp2.1/System.Collections.Concurrent.dll": {}, "ref/netcoreapp2.1/System.Collections.Immutable.dll": {}, "ref/netcoreapp2.1/System.Collections.NonGeneric.dll": {}, "ref/netcoreapp2.1/System.Collections.Specialized.dll": {}, "ref/netcoreapp2.1/System.Collections.dll": {}, "ref/netcoreapp2.1/System.ComponentModel.Annotations.dll": {}, "ref/netcoreapp2.1/System.ComponentModel.DataAnnotations.dll": {}, "ref/netcoreapp2.1/System.ComponentModel.EventBasedAsync.dll": {}, "ref/netcoreapp2.1/System.ComponentModel.Primitives.dll": {}, "ref/netcoreapp2.1/System.ComponentModel.TypeConverter.dll": {}, "ref/netcoreapp2.1/System.ComponentModel.dll": {}, "ref/netcoreapp2.1/System.Configuration.dll": {}, "ref/netcoreapp2.1/System.Console.dll": {}, "ref/netcoreapp2.1/System.Core.dll": {}, "ref/netcoreapp2.1/System.Data.Common.dll": {}, "ref/netcoreapp2.1/System.Data.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.Contracts.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.Debug.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.DiagnosticSource.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.FileVersionInfo.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.Process.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.StackTrace.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.TextWriterTraceListener.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.Tools.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.TraceSource.dll": {}, "ref/netcoreapp2.1/System.Diagnostics.Tracing.dll": {}, "ref/netcoreapp2.1/System.Drawing.Primitives.dll": {}, "ref/netcoreapp2.1/System.Drawing.dll": {}, "ref/netcoreapp2.1/System.Dynamic.Runtime.dll": {}, "ref/netcoreapp2.1/System.Globalization.Calendars.dll": {}, "ref/netcoreapp2.1/System.Globalization.Extensions.dll": {}, "ref/netcoreapp2.1/System.Globalization.dll": {}, "ref/netcoreapp2.1/System.IO.Compression.Brotli.dll": {}, "ref/netcoreapp2.1/System.IO.Compression.FileSystem.dll": {}, "ref/netcoreapp2.1/System.IO.Compression.ZipFile.dll": {}, "ref/netcoreapp2.1/System.IO.Compression.dll": {}, "ref/netcoreapp2.1/System.IO.FileSystem.DriveInfo.dll": {}, "ref/netcoreapp2.1/System.IO.FileSystem.Primitives.dll": {}, "ref/netcoreapp2.1/System.IO.FileSystem.Watcher.dll": {}, "ref/netcoreapp2.1/System.IO.FileSystem.dll": {}, "ref/netcoreapp2.1/System.IO.IsolatedStorage.dll": {}, "ref/netcoreapp2.1/System.IO.MemoryMappedFiles.dll": {}, "ref/netcoreapp2.1/System.IO.Pipes.dll": {}, "ref/netcoreapp2.1/System.IO.UnmanagedMemoryStream.dll": {}, "ref/netcoreapp2.1/System.IO.dll": {}, "ref/netcoreapp2.1/System.Linq.Expressions.dll": {}, "ref/netcoreapp2.1/System.Linq.Parallel.dll": {}, "ref/netcoreapp2.1/System.Linq.Queryable.dll": {}, "ref/netcoreapp2.1/System.Linq.dll": {}, "ref/netcoreapp2.1/System.Memory.dll": {}, "ref/netcoreapp2.1/System.Net.Http.dll": {}, "ref/netcoreapp2.1/System.Net.HttpListener.dll": {}, "ref/netcoreapp2.1/System.Net.Mail.dll": {}, "ref/netcoreapp2.1/System.Net.NameResolution.dll": {}, "ref/netcoreapp2.1/System.Net.NetworkInformation.dll": {}, "ref/netcoreapp2.1/System.Net.Ping.dll": {}, "ref/netcoreapp2.1/System.Net.Primitives.dll": {}, "ref/netcoreapp2.1/System.Net.Requests.dll": {}, "ref/netcoreapp2.1/System.Net.Security.dll": {}, "ref/netcoreapp2.1/System.Net.ServicePoint.dll": {}, "ref/netcoreapp2.1/System.Net.Sockets.dll": {}, "ref/netcoreapp2.1/System.Net.WebClient.dll": {}, "ref/netcoreapp2.1/System.Net.WebHeaderCollection.dll": {}, "ref/netcoreapp2.1/System.Net.WebProxy.dll": {}, "ref/netcoreapp2.1/System.Net.WebSockets.Client.dll": {}, "ref/netcoreapp2.1/System.Net.WebSockets.dll": {}, "ref/netcoreapp2.1/System.Net.dll": {}, "ref/netcoreapp2.1/System.Numerics.Vectors.dll": {}, "ref/netcoreapp2.1/System.Numerics.dll": {}, "ref/netcoreapp2.1/System.ObjectModel.dll": {}, "ref/netcoreapp2.1/System.Reflection.DispatchProxy.dll": {}, "ref/netcoreapp2.1/System.Reflection.Emit.ILGeneration.dll": {}, "ref/netcoreapp2.1/System.Reflection.Emit.Lightweight.dll": {}, "ref/netcoreapp2.1/System.Reflection.Emit.dll": {}, "ref/netcoreapp2.1/System.Reflection.Extensions.dll": {}, "ref/netcoreapp2.1/System.Reflection.Metadata.dll": {}, "ref/netcoreapp2.1/System.Reflection.Primitives.dll": {}, "ref/netcoreapp2.1/System.Reflection.TypeExtensions.dll": {}, "ref/netcoreapp2.1/System.Reflection.dll": {}, "ref/netcoreapp2.1/System.Resources.Reader.dll": {}, "ref/netcoreapp2.1/System.Resources.ResourceManager.dll": {}, "ref/netcoreapp2.1/System.Resources.Writer.dll": {}, "ref/netcoreapp2.1/System.Runtime.CompilerServices.VisualC.dll": {}, "ref/netcoreapp2.1/System.Runtime.Extensions.dll": {}, "ref/netcoreapp2.1/System.Runtime.Handles.dll": {}, "ref/netcoreapp2.1/System.Runtime.InteropServices.RuntimeInformation.dll": {}, "ref/netcoreapp2.1/System.Runtime.InteropServices.WindowsRuntime.dll": {}, "ref/netcoreapp2.1/System.Runtime.InteropServices.dll": {}, "ref/netcoreapp2.1/System.Runtime.Loader.dll": {}, "ref/netcoreapp2.1/System.Runtime.Numerics.dll": {}, "ref/netcoreapp2.1/System.Runtime.Serialization.Formatters.dll": {}, "ref/netcoreapp2.1/System.Runtime.Serialization.Json.dll": {}, "ref/netcoreapp2.1/System.Runtime.Serialization.Primitives.dll": {}, "ref/netcoreapp2.1/System.Runtime.Serialization.Xml.dll": {}, "ref/netcoreapp2.1/System.Runtime.Serialization.dll": {}, "ref/netcoreapp2.1/System.Runtime.dll": {}, "ref/netcoreapp2.1/System.Security.Claims.dll": {}, "ref/netcoreapp2.1/System.Security.Cryptography.Algorithms.dll": {}, "ref/netcoreapp2.1/System.Security.Cryptography.Csp.dll": {}, "ref/netcoreapp2.1/System.Security.Cryptography.Encoding.dll": {}, "ref/netcoreapp2.1/System.Security.Cryptography.Primitives.dll": {}, "ref/netcoreapp2.1/System.Security.Cryptography.X509Certificates.dll": {}, "ref/netcoreapp2.1/System.Security.Principal.dll": {}, "ref/netcoreapp2.1/System.Security.SecureString.dll": {}, "ref/netcoreapp2.1/System.Security.dll": {}, "ref/netcoreapp2.1/System.ServiceModel.Web.dll": {}, "ref/netcoreapp2.1/System.ServiceProcess.dll": {}, "ref/netcoreapp2.1/System.Text.Encoding.Extensions.dll": {}, "ref/netcoreapp2.1/System.Text.Encoding.dll": {}, "ref/netcoreapp2.1/System.Text.RegularExpressions.dll": {}, "ref/netcoreapp2.1/System.Threading.Overlapped.dll": {}, "ref/netcoreapp2.1/System.Threading.Tasks.Dataflow.dll": {}, "ref/netcoreapp2.1/System.Threading.Tasks.Extensions.dll": {}, "ref/netcoreapp2.1/System.Threading.Tasks.Parallel.dll": {}, "ref/netcoreapp2.1/System.Threading.Tasks.dll": {}, "ref/netcoreapp2.1/System.Threading.Thread.dll": {}, "ref/netcoreapp2.1/System.Threading.ThreadPool.dll": {}, "ref/netcoreapp2.1/System.Threading.Timer.dll": {}, "ref/netcoreapp2.1/System.Threading.dll": {}, "ref/netcoreapp2.1/System.Transactions.Local.dll": {}, "ref/netcoreapp2.1/System.Transactions.dll": {}, "ref/netcoreapp2.1/System.ValueTuple.dll": {}, "ref/netcoreapp2.1/System.Web.HttpUtility.dll": {}, "ref/netcoreapp2.1/System.Web.dll": {}, "ref/netcoreapp2.1/System.Windows.dll": {}, "ref/netcoreapp2.1/System.Xml.Linq.dll": {}, "ref/netcoreapp2.1/System.Xml.ReaderWriter.dll": {}, "ref/netcoreapp2.1/System.Xml.Serialization.dll": {}, "ref/netcoreapp2.1/System.Xml.XDocument.dll": {}, "ref/netcoreapp2.1/System.Xml.XPath.XDocument.dll": {}, "ref/netcoreapp2.1/System.Xml.XPath.dll": {}, "ref/netcoreapp2.1/System.Xml.XmlDocument.dll": {}, "ref/netcoreapp2.1/System.Xml.XmlSerializer.dll": {}, "ref/netcoreapp2.1/System.Xml.dll": {}, "ref/netcoreapp2.1/System.dll": {}, "ref/netcoreapp2.1/WindowsBase.dll": {}, "ref/netcoreapp2.1/mscorlib.dll": {}, "ref/netcoreapp2.1/netstandard.dll": {} }, "compileOnly": true }, "Microsoft.NETCore.DotNetAppHost/2.1.0": { "compileOnly": true }, "Microsoft.NETCore.DotNetHostPolicy/2.1.0": { "dependencies": { "Microsoft.NETCore.DotNetHostResolver": "2.1.0" }, "compileOnly": true }, "Microsoft.NETCore.DotNetHostResolver/2.1.0": { "dependencies": { "Microsoft.NETCore.DotNetAppHost": "2.1.0" }, "compileOnly": true }, "Microsoft.NETCore.Platforms/2.1.0": { "compileOnly": true }, "Microsoft.NETCore.Targets/2.1.0": { "compileOnly": true }, "NETStandard.Library/2.0.3": { "dependencies": { "Microsoft.NETCore.Platforms": "2.1.0" }, "compileOnly": true } } }, "libraries": { "IntelliTrader.Web/1.0.0": { "type": "project", "serviceable": false, "sha512": "" }, "Autofac/4.8.1": { "type": "package", "serviceable": true, "sha512": "sha512-aIT9rupCOdab5RMfxvWTBmOxGU77tLqmvSF4V89SzV6oQcJrtuKw/Xp55xy9EijSktbMka55SbroAPOyT+lziw==", "path": "autofac/4.8.1", "hashPath": "autofac.4.8.1.nupkg.sha512" }, "Microsoft.AspNetCore.Antiforgery/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-BKDp2thf1k3Q2XBSIxC0TvHLvGFOr3ga3DdsxOJNTQ2MEvCuqlNFAoBxXIXWtvP9EHNfLbmKA0+VF7nBqXTlYg==", "path": "microsoft.aspnetcore.antiforgery/2.1.1", "hashPath": "microsoft.aspnetcore.antiforgery.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Authentication/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-WgbDLOGoyX0/EoUdAlihMaKIpON6LwCYZ8fiPhZZe+qdCJhvl1aTBmJ/carHcv3NJGT+ETuq2ppYQr7PKLq1CQ==", "path": "microsoft.aspnetcore.authentication/2.1.1", "hashPath": "microsoft.aspnetcore.authentication.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Authentication.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-kl1yZmNeUMm9/kWtqoOvIATBavqHPwJICl0FA9rpvNqETqeTgakAbbY25TdG82wKKbjo4LpqZ0YCHwktNPaR2Q==", "path": "microsoft.aspnetcore.authentication.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.authentication.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Authentication.Cookies/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-Yz9dgcZvZ+OJjJ8ZX/+DtgY0+9ZuKzNO0cHkDUdQubY4W4Ozn5e194s70lNQiiEGJjah9hd/5yuayPAePiz7DQ==", "path": "microsoft.aspnetcore.authentication.cookies/2.1.1", "hashPath": "microsoft.aspnetcore.authentication.cookies.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Authentication.Core/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-I7CfHtUAwVH67ayCG9ZrkRI5si0yOlttb0ltMR36dMwXfPR9CYab0o9PyWfTOfGIT9VQ+UgAEH9U9+jVoEjPeg==", "path": "microsoft.aspnetcore.authentication.core/2.1.1", "hashPath": "microsoft.aspnetcore.authentication.core.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Authorization/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-z/5haIkI/G2NcCMO288l6l7Jy3BDqzZjHLb2VxjCfj4NKRVv6KlsDD7nGIyAtAbDVKnbOsGBXF6xwhyo4aFGBw==", "path": "microsoft.aspnetcore.authorization/2.1.1", "hashPath": "microsoft.aspnetcore.authorization.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Authorization.Policy/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-ipuhLj35k90+q6GbBuJaouPDLGwaJilBUUE+y0rtGL+yncCtA1gYFrs3jZ+tRX/zNqlVtlAb1u7wXm5NJ/TkQw==", "path": "microsoft.aspnetcore.authorization.policy/2.1.1", "hashPath": "microsoft.aspnetcore.authorization.policy.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Connections.Abstractions/2.1.2": { "type": "package", "serviceable": true, "sha512": "sha512-O/dAVpldQyJEzzrY1f6Ki+s4DeAk8qtvcSK8Pk+zJLYB9tZKdWMQ68ob+fy7CsYCdLyhbT/vro0TSBK1teit7g==", "path": "microsoft.aspnetcore.connections.abstractions/2.1.2", "hashPath": "microsoft.aspnetcore.connections.abstractions.2.1.2.nupkg.sha512" }, "Microsoft.AspNetCore.Cors/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-ajz3/gjo4OYDFId5nJUrBAYJhKW3sJrK5+dLJ3ynTuVyGwY5me3QICukzMeADSKNV+JapSrPKLXIythHwDrQjA==", "path": "microsoft.aspnetcore.cors/2.1.1", "hashPath": "microsoft.aspnetcore.cors.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Cryptography.Internal/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-9X49e4ZTv6ipL/Yh1GvVxpgh+ghWMHi+PPE3tQI2HRgG6Jixvmt8LgT/KvAvfgYEDnjsSTRyt/arrHsekHwfMA==", "path": "microsoft.aspnetcore.cryptography.internal/2.1.1", "hashPath": "microsoft.aspnetcore.cryptography.internal.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.DataProtection/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-561yQw2Xu5DH05p6uv4G6dD0tfO2KeNuFz/kPREHHFzOk4PF3tdmH9LjCz2fX8eyOvgvfiLSib3atE7thRvZDQ==", "path": "microsoft.aspnetcore.dataprotection/2.1.1", "hashPath": "microsoft.aspnetcore.dataprotection.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.DataProtection.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-94UHZlJQUeCeCsrDNrEVDO7nOoFsr1KSetcHAttPA6DDe80XJ57wbWUpzxjoGRimoGG2yS95n7M0bueZCMD7ag==", "path": "microsoft.aspnetcore.dataprotection.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.dataprotection.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Diagnostics/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-F9GjtKSe4HeOqZJjnnI110wDcvsY0aguALGswbr+R3iuw6X+Mzko7S/Vx7LxQXxInOCJoxnNEkd7Kf59dFFSRg==", "path": "microsoft.aspnetcore.diagnostics/2.1.1", "hashPath": "microsoft.aspnetcore.diagnostics.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Diagnostics.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-rLn97UtnaXvD1E8K2UFQg5MBZ/D6KLuMZEEt47qkIIEsEQar84yIlR3HdDDF7ovJ/Bg546EyJXHxXvi7t6G7yw==", "path": "microsoft.aspnetcore.diagnostics.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.diagnostics.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Hosting/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-rO2JSJGuHJMYE68vm72bFI+PEj1e6zgv9r3izNMEMwyGtjsEDFSHALoGqffnehY63TKqpXdAKElKzPV0UYrMqA==", "path": "microsoft.aspnetcore.hosting/2.1.1", "hashPath": "microsoft.aspnetcore.hosting.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Hosting.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-FFZxJAK3sV9JxZ7YP47upycv6VZOcNvJLiLM0FXfvlrb67RC9y4AjCUX1RvI0W1n1v6GMZhWSNb3KYs+O6s26g==", "path": "microsoft.aspnetcore.hosting.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.hosting.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-xqfxC5t1Jk4ZOQN5xfR2Q0nqTOTN5R6FORk4LqjEzmfX8NDdEsds+Fj6d9bMYqhPWZ4ATRAi8RmaUKYPQuAWbQ==", "path": "microsoft.aspnetcore.hosting.server.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.hosting.server.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Html.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-tPZG0aA3V8tljooIgKhAiVxu7ZnAnL7QPzz3uxQgs4v7vwwCZTigzh2PIL4QRtezlGFk1jn7PbOtxi+FsmEe0g==", "path": "microsoft.aspnetcore.html.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.html.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Http/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-u8Fmky/nirrxOU1gBGh97J5gPoniWDc1QiT+J0EFuXJWcFo3BgPGiv7RLvYCi89QpLgIt5CkkPqTkPnWz0eaSA==", "path": "microsoft.aspnetcore.http/2.1.1", "hashPath": "microsoft.aspnetcore.http.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Http.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-0TPQgjRy2xJ75GcK18vvrT6/zCtSAWUEBSskSJN/lY0zuvQx2or8lzwr0TdKyMNK8A8MLP4QMLPqL9NOAxe0yg==", "path": "microsoft.aspnetcore.http.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.http.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Http.Extensions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-0dgKLajNfwElW6fLElwjo+fEyfhXdSN74QeXhOUgPam5UIbU3EBQU/+xD83MnfprAiUPDWHqueTKuB8oa/cjNQ==", "path": "microsoft.aspnetcore.http.extensions/2.1.1", "hashPath": "microsoft.aspnetcore.http.extensions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Http.Features/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-cMnTXRH+8T7GLht6cXRCMmN1HaYfXti2WEUdXqMUuyJgi4oH9cmzW4nECSBkQjsCs5O06BphyDDDAsTW/zQmpg==", "path": "microsoft.aspnetcore.http.features/2.1.1", "hashPath": "microsoft.aspnetcore.http.features.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.JsonPatch/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-T5kx4u+0CH5bD3hB+QEozR4MmLZ7CDGdm0+OD1wxyQBJKNNA6jRSJmbvsZ8nmOEwoGtAfHdXLYM0r3/Zw6J4JQ==", "path": "microsoft.aspnetcore.jsonpatch/2.1.1", "hashPath": "microsoft.aspnetcore.jsonpatch.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Localization/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-oy13Ppp0iBLHAzq03R5tEBNTAfatboreqW7YEMhVA2fu6L0KLmBk3njHc0FJaFnwZwCbmPnRtr81J8A7NWqQuQ==", "path": "microsoft.aspnetcore.localization/2.1.1", "hashPath": "microsoft.aspnetcore.localization.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-3mHitdj9MClvbFThDsVhojGH2PxWWxhJNFzFwNnofSdORrnRby9bikM+HCqUOz2gvxnyYz5jsgbA88+CGkNy4A==", "path": "microsoft.aspnetcore.mvc/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-/XgeeXi0LrykMlMCNMQftj2XyEua4JT5AFAt3D3xE6KChx0PydXTFiwQtDvbGpNvarPQWWdyEfq1rKlgyVGlXA==", "path": "microsoft.aspnetcore.mvc.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.ApiExplorer/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-GGPbYZfzJvu6rigtCN0FRQD4B8ERmMO+grCyf/lfQhmqK9cTfhDcU8Zfw75SXrQ3Ity1lSvYpf26XeFVIi5Y5A==", "path": "microsoft.aspnetcore.mvc.apiexplorer/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.apiexplorer.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.Core/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-QoYLsJHrN7LNnL1LWzSGzQm3v/1ERI5csb4LSzNYm71EcCG8SWckw76GgXNx6mjsJXfxsvoqRAovnLQKCCBtvA==", "path": "microsoft.aspnetcore.mvc.core/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.core.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.Cors/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-a04jcvPbG6IfaugJe3CS59ZhSRAVLmwVEGDLp4wGuR4/9yW3T4mCZgqcSQz+5921j/hRGn1Jwu/b05bWkg+wBg==", "path": "microsoft.aspnetcore.mvc.cors/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.cors.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.DataAnnotations/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-3/LdPk7u3VitfUxVu+forzb+YFa/G4tqFDQKG20mMHrAnE7ranDUhqURD7qoy8JFLRWdhvvdBhUJaATfvvmTVg==", "path": "microsoft.aspnetcore.mvc.dataannotations/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.dataannotations.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.Formatters.Json/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-nBzpBR0Ei/4L63+ylGS6P4gP+u+/S1cIvUU4+G+4Rk+vtzNT5KsoFP9TfCvW8hGQ6ShehjT7wXMuci/D2SbCQQ==", "path": "microsoft.aspnetcore.mvc.formatters.json/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.formatters.json.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.Localization/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-JN/d/T8JUYoF/YMBupIu92ZcP9PcYfLLQqIZWvfyJrNNftgXENAHMLn1999POEzG44RjGouWdioSH8QZJ1mTTQ==", "path": "microsoft.aspnetcore.mvc.localization/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.localization.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.Razor/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-9WCfQX8+xZN8pzRK8ZxCJw/3lpsKsg3iQvFr6CRz4UtayLEoq/uzLKL5xvY8fj1rVJjt3wBh+YBhheB/196QSg==", "path": "microsoft.aspnetcore.mvc.razor/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.razor.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.Razor.Extensions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-w/4GAxZS5y9CnlIO4z04sC7I+cLVVYsvI+hC+Thh2vy5AQxNZj9ZIxmdIPtvqQfZ2JdURQ7cpBsr8pzf4YhTEA==", "path": "microsoft.aspnetcore.mvc.razor.extensions/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.razor.extensions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.RazorPages/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-icxhGYO1z5IQsrmJhbIJUHM2a0mTK7g1kdPR/mnB5L4r35im8ElX0449AFN3KlA0C00E6mzXVe1CCJ3wO+TUxQ==", "path": "microsoft.aspnetcore.mvc.razorpages/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.razorpages.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.TagHelpers/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-wLHZ9TUdD9Gl2rVihrNGmRJ1LGTjiRzPM4d78efClOpFJwhMaHCnr9ktfQhnJX4XQj0w22XvPPCV0GxSrVp4Lg==", "path": "microsoft.aspnetcore.mvc.taghelpers/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.taghelpers.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Mvc.ViewFeatures/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-4F4uu3Hh5pgQ/2XkKgG2XEfPIvzUUjpOrSPIdOpMzxloTfYM/jK6xEW6kM9DE5vYhyW9EE02sngRBh8cmU0vng==", "path": "microsoft.aspnetcore.mvc.viewfeatures/2.1.1", "hashPath": "microsoft.aspnetcore.mvc.viewfeatures.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Razor/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-oDxJTufrOF2Y7g+p2jU5+2xtrcsb3KX20pH/KosLW5rbsJMAqaOwprI6gJlBQCGtMCYl/MbnC45ZObPmzyI0NQ==", "path": "microsoft.aspnetcore.razor/2.1.1", "hashPath": "microsoft.aspnetcore.razor.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Razor.Design/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-f8PKGcxiezL1RVqmnmrazj24Mj4KCTSXqwdotl7Lc+82h8iLV7ItxEIShTJakG7M9iw0ZuCocM0J/IhYesdQrg==", "path": "microsoft.aspnetcore.razor.design/2.1.1", "hashPath": "microsoft.aspnetcore.razor.design.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Razor.Language/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-5HX7/SguN9F8cdJ6GBBFJauEii/k6XPuI1gHucOcOBKKetgm4nG/xrHzRGSBTxmc1rbCcVKrBl10/PYItE7JyA==", "path": "microsoft.aspnetcore.razor.language/2.1.1", "hashPath": "microsoft.aspnetcore.razor.language.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Razor.Runtime/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-dGublvci7Lwu8gAegh81YXATyKGupWHb5RDHPsIO/Ct++xG7Lv9/6nNbci05sqYienZgprDbTAH8G7PmBCpIKQ==", "path": "microsoft.aspnetcore.razor.runtime/2.1.1", "hashPath": "microsoft.aspnetcore.razor.runtime.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.ResponseCaching.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-F2/eDBTwGdTdQ+YPrlf7DBprzbHVZmZqnCTkHT6Jge7MQDu0xgUmDfNyBUzg9jn38RSKnDp6RWLQSJ6yqsYdIQ==", "path": "microsoft.aspnetcore.responsecaching.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.responsecaching.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Routing/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-BnVEKMGIkRcZecG3zR+tl9tYGkViz1k/WzYVNRfdaAN0LeuSabNP0NlG037oz+pDPsLzzNkFeLSOh/w0AKLaig==", "path": "microsoft.aspnetcore.routing/2.1.1", "hashPath": "microsoft.aspnetcore.routing.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Routing.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-+Yxsy/ZcCthcziktuhfC6WpQ/cZzgD/IsQ96xefNKrCzIm9jXjfNK3ONsoScvyFFihNohp7zAVPiic5J6CvUDw==", "path": "microsoft.aspnetcore.routing.abstractions/2.1.1", "hashPath": "microsoft.aspnetcore.routing.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.Server.Kestrel/2.1.2": { "type": "package", "serviceable": true, "sha512": "sha512-ol4rQS7QpGBtfcHY39ecVnAg1Fe0MXTIZN/hm4ldJAXE2+PmH7BnLl1yJrZB5MMeXHYsU+v1N0+iSkX5jJm2uw==", "path": "microsoft.aspnetcore.server.kestrel/2.1.2", "hashPath": "microsoft.aspnetcore.server.kestrel.2.1.2.nupkg.sha512" }, "Microsoft.AspNetCore.Server.Kestrel.Core/2.1.2": { "type": "package", "serviceable": true, "sha512": "sha512-tig0vQgH1hvY3We458hCRBjJfu0zc+sqGZxTboXde8pT932MdsxxUzimxPCHdg3RBRRzVct1E1KtMb6c6M5Pxw==", "path": "microsoft.aspnetcore.server.kestrel.core/2.1.2", "hashPath": "microsoft.aspnetcore.server.kestrel.core.2.1.2.nupkg.sha512" }, "Microsoft.AspNetCore.Server.Kestrel.Https/2.1.2": { "type": "package", "serviceable": true, "sha512": "sha512-Z+GhgasOk1tr3g5NVkUmVqHpbi+ORqkTenErnBTF1DjgXileomxweijL2StNvmv1dtJhxhclZpIGePpWIKAAfA==", "path": "microsoft.aspnetcore.server.kestrel.https/2.1.2", "hashPath": "microsoft.aspnetcore.server.kestrel.https.2.1.2.nupkg.sha512" }, "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.1.2": { "type": "package", "serviceable": true, "sha512": "sha512-wtlxVAdrp6BsBi3/A3yeIOygZl1m2Wn2GOYBjngp5tTbhMJZm0bVO+xPcw2bOKUL1eQHUQwRsJB5kabPKphotg==", "path": "microsoft.aspnetcore.server.kestrel.transport.abstractions/2.1.2", "hashPath": "microsoft.aspnetcore.server.kestrel.transport.abstractions.2.1.2.nupkg.sha512" }, "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.1.2": { "type": "package", "serviceable": true, "sha512": "sha512-PHCpMsg8aSuNFkgXX9f+HIkYNpWMDj5l+hFni4Zyuv3hv2xfRdehqvEZTAsYPRbRWy5hyewCSZ3Kh/fifLQc7g==", "path": "microsoft.aspnetcore.server.kestrel.transport.sockets/2.1.2", "hashPath": "microsoft.aspnetcore.server.kestrel.transport.sockets.2.1.2.nupkg.sha512" }, "Microsoft.AspNetCore.StaticFiles/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-3xumS58evfsC4cd8OXtYRafbwuVk5c37dsGQ1E1m0wZvRVUXScRWkTGdcPJcijoImlhoQK2pj6sY7NFMc5PfbQ==", "path": "microsoft.aspnetcore.staticfiles/2.1.1", "hashPath": "microsoft.aspnetcore.staticfiles.2.1.1.nupkg.sha512" }, "Microsoft.AspNetCore.WebUtilities/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-gvCdObgQDLdZ9enyFQuPb3Rae6QyzZAPgHiv5JhYjORLMW1UNgWXvdqLov6iGtnyG+BBCavPooW9ScWGQCJHLg==", "path": "microsoft.aspnetcore.webutilities/2.1.1", "hashPath": "microsoft.aspnetcore.webutilities.2.1.1.nupkg.sha512" }, "Microsoft.CodeAnalysis.Analyzers/1.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-6csv1zVOCb3tncoRbuYclgO5qksGIypQbUb3pofrcWVibbT3Bpq0rx19Xf5Vm1l1MzmY2HJiUY1JubL0YZvFNA==", "path": "microsoft.codeanalysis.analyzers/1.1.0", "hashPath": "microsoft.codeanalysis.analyzers.1.1.0.nupkg.sha512" }, "Microsoft.CodeAnalysis.Common/2.8.0": { "type": "package", "serviceable": true, "sha512": "sha512-lYUBqh3OD3iEQqxt9KB472VzgOnEKoUVG4Lx5Xw4oJe9dZtITkHFtct+T73jH3FOASFI1NSzzP5MBM0c9zZspA==", "path": "microsoft.codeanalysis.common/2.8.0", "hashPath": "microsoft.codeanalysis.common.2.8.0.nupkg.sha512" }, "Microsoft.CodeAnalysis.CSharp/2.8.0": { "type": "package", "serviceable": true, "sha512": "sha512-+4CHAwHMwLO5GRqPJ7Khv2Ny//omhukPKP3Ny/d2XDpt11bX35zb9pTziwZN0eNvxj6a46joIdHEYQ1JsekI3w==", "path": "microsoft.codeanalysis.csharp/2.8.0", "hashPath": "microsoft.codeanalysis.csharp.2.8.0.nupkg.sha512" }, "Microsoft.CodeAnalysis.Razor/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-D5zUSmQHsgKosYlWLQjs6uXn4n7llEdUwFhJz7EIwR16ge18q8p8BJ547out9ScnMDuwHA8MeCPe8WMwCaFAPw==", "path": "microsoft.codeanalysis.razor/2.1.1", "hashPath": "microsoft.codeanalysis.razor.2.1.1.nupkg.sha512" }, "Microsoft.CSharp/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-EGoBmf3Na2ppbhPePDE9PlX81r1HuOZH5twBrq7couJZiPTjUnD3648balerQJO6EJ8Sj+43+XuRwQ7r+3tE3w==", "path": "microsoft.csharp/4.5.0", "hashPath": "microsoft.csharp.4.5.0.nupkg.sha512" }, "Microsoft.DotNet.PlatformAbstractions/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-wkCXkBS0q+5hsbeikjfsHCGP3nNe1L1MrDEBPCBKm+4UH8nXqHLxDZuBrTYaVY85CGIx2y1qW90nO6b+ORAfrA==", "path": "microsoft.dotnet.platformabstractions/2.1.0", "hashPath": "microsoft.dotnet.platformabstractions.2.1.0.nupkg.sha512" }, "Microsoft.Extensions.Caching.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-gcPRTtchou4pIEdLYhh9xoBDjwCaCLiTHJaFN2IWJCP+TGJcIHQYblPMftw6fajHER9ZrvPO5RYZUyLmH1eNIA==", "path": "microsoft.extensions.caching.abstractions/2.1.1", "hashPath": "microsoft.extensions.caching.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Caching.Memory/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-KV2w9nelcxgl1Y028qmexCcgBK+CtZ18fE2eIypB1lUtLOGBrzP+XhcJTxBYwXPnYPkxazqdzcOfIRxz/Bq2uQ==", "path": "microsoft.extensions.caching.memory/2.1.1", "hashPath": "microsoft.extensions.caching.memory.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Configuration/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-1JaydycXzbfAExlsD7XIWykzVnU/wZM86KzrHyGlXuxqnqzcWSXLJn4Ejn8bDnq07CEJNZ+GjsxWKlJ8kFfnvQ==", "path": "microsoft.extensions.configuration/2.1.1", "hashPath": "microsoft.extensions.configuration.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Configuration.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-9EMhOWU2eOQOtMIJ+vfwKJpnLRc1Wl3vXu8qXeevA91cSY4j3WvArmF7ApGtJwa7yKewJTvlQlBSn9OSnLFg6Q==", "path": "microsoft.extensions.configuration.abstractions/2.1.1", "hashPath": "microsoft.extensions.configuration.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Configuration.Binder/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-t7KFAv6AxyUsZj9QN8FAbusg+X5baCELl+XtscyuP1IGUv5UctyY7/rNZLyiKaV7HhAcDQ1zC5ZQNQQFn6JpAA==", "path": "microsoft.extensions.configuration.binder/2.1.1", "hashPath": "microsoft.extensions.configuration.binder.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Configuration.EnvironmentVariables/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-rDFRChBvs6sPGC+JjshKsP4kWRvsG8Y9MQKduDu60RWnJpFiIpQ7HK2K9sPrCL1MaYEk894PUkiZ5Xdsm9cPvg==", "path": "microsoft.extensions.configuration.environmentvariables/2.1.1", "hashPath": "microsoft.extensions.configuration.environmentvariables.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Configuration.FileExtensions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-JnhKotPCs1+X4CPSsHOk8CpxmBeIS/vIXYewsoM8XflXNhpzMe1gfIckQyuRKyORlGaNFEBr4WrPjpZ159bS/Q==", "path": "microsoft.extensions.configuration.fileextensions/2.1.1", "hashPath": "microsoft.extensions.configuration.fileextensions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Configuration.Json/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-f6KcI9v0GVA4YL/ExoxrEfeQv9La3hyQnySfgxGkFtMeDJIUun0ANoMjspbdpXXnuaScwgbQ2mFE3lJHt9lpJw==", "path": "microsoft.extensions.configuration.json/2.1.1", "hashPath": "microsoft.extensions.configuration.json.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.DependencyInjection/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-2nshYaLTn73Ie+/yTkb7EZIXwQeFIXsYCBy/jSY9bMayYykGNjdWa25frayhuPAGVbZpEgfgp3d4JRVEuVyEqQ==", "path": "microsoft.extensions.dependencyinjection/2.1.1", "hashPath": "microsoft.extensions.dependencyinjection.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-PW1596sF97gpIc1JuUuYvTmeLfeqC5whbWPsWgJhN0fdwz683him3b/HB0dqhFesVssOjnnA0fEz4+S0gUeBqA==", "path": "microsoft.extensions.dependencyinjection.abstractions/2.1.1", "hashPath": "microsoft.extensions.dependencyinjection.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.DependencyModel/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-3KPT6CLH0VEGr2um9aG1rYTmqfMVlkRuueFpN6AxeIKpcMA4OVHf4aNpgYXZ6oF+x4uh9VhK/66FgPCd1mMlnQ==", "path": "microsoft.extensions.dependencymodel/2.1.0", "hashPath": "microsoft.extensions.dependencymodel.2.1.0.nupkg.sha512" }, "Microsoft.Extensions.FileProviders.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-qOJP+VAlXDeMQSJ6iflW62bEsN3S1NJIPHmhKFA9L37yU+jce2wbwesA7sDe9WdJ8+SoKtLnHPUxvOyQrAcRCA==", "path": "microsoft.extensions.fileproviders.abstractions/2.1.1", "hashPath": "microsoft.extensions.fileproviders.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.FileProviders.Composite/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-SovLUACJ3C+iRlHo4VdZw0IDX+v7+32paTJf7v5ZyzyWqijUkDYXr81gL7tkCfCkJmBYnrc6bScoj2Eaxlrudw==", "path": "microsoft.extensions.fileproviders.composite/2.1.1", "hashPath": "microsoft.extensions.fileproviders.composite.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.FileProviders.Physical/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-pbT/J3B686Xgktv5WH11FbcbZXDmBQuCN3ce8IKIF+DpOk3p0RgUPrOXcYNp81TyH+K/5Cosr4VFVjYMoirNDg==", "path": "microsoft.extensions.fileproviders.physical/2.1.1", "hashPath": "microsoft.extensions.fileproviders.physical.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.FileSystemGlobbing/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-Pu/O8jBc7QlEmqmbDGVosuDlyzGspMuKc71rOsJigwGMF5574aWYw9uRMX+ho1dmbnL502ZYHo6PlBP3IXkm5A==", "path": "microsoft.extensions.filesystemglobbing/2.1.1", "hashPath": "microsoft.extensions.filesystemglobbing.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Hosting.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-v7mPlJ68Dsev9gn6w5tJJZI798r6gCmwKBv0pwJ5PunLEITYjrv1+QJ/wYkp7KuRcr8VRUML8mJg/mgUjgHggA==", "path": "microsoft.extensions.hosting.abstractions/2.1.1", "hashPath": "microsoft.extensions.hosting.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Localization/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-XPVATgcnzWwo6NYXsZfiEBSSFWWOEdFMn099BIlJCgwVSTLdZD130xRFH4wGXg5sMos3xXsBLv1fffQ67Ju+qg==", "path": "microsoft.extensions.localization/2.1.1", "hashPath": "microsoft.extensions.localization.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Localization.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-V1znqxUEDHAfnCDXLsfrbY+RmtrFkJqOFhVBOIrcqQMp6MFJvIV9QpDTMq8JzqYc++aAraIoUEAsAwoa8otlOw==", "path": "microsoft.extensions.localization.abstractions/2.1.1", "hashPath": "microsoft.extensions.localization.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Logging/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-x4/RzeReQSIi4nVpOjXEySm/xUSr6lBjuecdYnlUboWxbLSm2j3vhFV5OLGRp3gfte3cRMdysMNa/wyZN0t/Tw==", "path": "microsoft.extensions.logging/2.1.1", "hashPath": "microsoft.extensions.logging.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Logging.Abstractions/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-QWFWKrdeoDSEr8nVJaBAVDMj24wnh9clGzDNmMdgHHRsOIwTUMeh4XljeZXJhIKPT00jWuzwEzn3uNxOtO4cYg==", "path": "microsoft.extensions.logging.abstractions/2.1.1", "hashPath": "microsoft.extensions.logging.abstractions.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.ObjectPool/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-FE4JmV6FEZdmqSKqvld5TRnvHfJfrw9QzvvZlAiTn+FCiq/1ZaQDpcYBRH7dMHFWIsYD6Z2UTsufdbCGznox8g==", "path": "microsoft.extensions.objectpool/2.1.1", "hashPath": "microsoft.extensions.objectpool.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Options/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-j0zOfTt1Qm+JDW2m+6Q/aj1m4C8+onudUu4ls/fN69VxruZkMWmX1bPKkbkYIPNNxJsf4k7FOkVq5o1vEFq9pQ==", "path": "microsoft.extensions.options/2.1.1", "hashPath": "microsoft.extensions.options.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.Primitives/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-Svz25/egj1TsNL4118jyMqkhDiu0l8QYWq2p52P4BBN0GbqwR18ZRIctSP5TTDJy0m0EFC8aB2FOVjGtvEGWSA==", "path": "microsoft.extensions.primitives/2.1.1", "hashPath": "microsoft.extensions.primitives.2.1.1.nupkg.sha512" }, "Microsoft.Extensions.WebEncoders/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-0fR5UV3qREnTpGiqUkz6p30gHzRNvZExgTpch0Gwc+lVUh7D2MBLK/2ohmsMnXp7ckYiEAHhEb9Z/NTUdajKXA==", "path": "microsoft.extensions.webencoders/2.1.1", "hashPath": "microsoft.extensions.webencoders.2.1.1.nupkg.sha512" }, "Microsoft.Net.Http.Headers/2.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-tNh1YCfZ943/d3WSE6cD57O05rhvi3lmKgwoi3zFg4wc/O/oec5FNHZmBCRau4GfzRC5zS/CBdOAkRwbvtZSaQ==", "path": "microsoft.net.http.headers/2.1.1", "hashPath": "microsoft.net.http.headers.2.1.1.nupkg.sha512" }, "Microsoft.Win32.Registry/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-vduxuHEqRgRrTE8wYG8Wxj/+6wwzddOmZzjKZx6rFMc/91aUBxI5etAFYxesoNaIja5NpgSTcnk6cN8BeYXf9A==", "path": "microsoft.win32.registry/4.5.0", "hashPath": "microsoft.win32.registry.4.5.0.nupkg.sha512" }, "Newtonsoft.Json/11.0.2": { "type": "package", "serviceable": true, "sha512": "sha512-IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ==", "path": "newtonsoft.json/11.0.2", "hashPath": "newtonsoft.json.11.0.2.nupkg.sha512" }, "Newtonsoft.Json.Bson/1.0.1": { "type": "package", "serviceable": true, "sha512": "sha512-5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", "path": "newtonsoft.json.bson/1.0.1", "hashPath": "newtonsoft.json.bson.1.0.1.nupkg.sha512" }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==", "path": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==", "path": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==", "path": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.native.System/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", "path": "runtime.native.system/4.3.0", "hashPath": "runtime.native.system.4.3.0.nupkg.sha512" }, "runtime.native.System.IO.Compression/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", "path": "runtime.native.system.io.compression/4.3.0", "hashPath": "runtime.native.system.io.compression.4.3.0.nupkg.sha512" }, "runtime.native.System.Net.Http/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", "path": "runtime.native.system.net.http/4.3.0", "hashPath": "runtime.native.system.net.http.4.3.0.nupkg.sha512" }, "runtime.native.System.Security.Cryptography.Apple/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", "path": "runtime.native.system.security.cryptography.apple/4.3.0", "hashPath": "runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512" }, "runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", "path": "runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==", "path": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==", "path": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==", "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.0", "hashPath": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512" }, "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==", "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==", "path": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==", "path": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==", "path": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==", "path": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.0", "hashPath": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "Serilog/2.7.1": { "type": "package", "serviceable": true, "sha512": "sha512-5eS6AD4hesOYvlwYqjntLaOonDnEiY22QiMUKueJHPrm5vm1k4tzXgOgCb2mJoMnj6l5Wt6AXV5liXzXymvarg==", "path": "serilog/2.7.1", "hashPath": "serilog.2.7.1.nupkg.sha512" }, "Serilog.Enrichers.Environment/2.1.2": { "type": "package", "serviceable": true, "sha512": "sha512-Uj3X4tGW8T38IIGCp/MxbHgg5vG3HN5myBQLIw2JTt87Gwv11NgZJGc4hunmFocQYny09CPVy2LRfiI6gAqQ4Q==", "path": "serilog.enrichers.environment/2.1.2", "hashPath": "serilog.enrichers.environment.2.1.2.nupkg.sha512" }, "Serilog.Filters.Expressions/2.0.0": { "type": "package", "serviceable": true, "sha512": "sha512-JhD2uV1s3ixF4L2dSB7t7jV5OKG8AEQOYtfTqSVkNm9X/g6zr1uoGH62XhDfCzEbyd5fiB9Rv4IXm+8m98Ao9Q==", "path": "serilog.filters.expressions/2.0.0", "hashPath": "serilog.filters.expressions.2.0.0.nupkg.sha512" }, "Serilog.Settings.Configuration/2.6.1": { "type": "package", "serviceable": true, "sha512": "sha512-d23bkPRrI/lxfe3FQ2C1KouU/3tBDhGhIHvmlW94rDeNductsu6wzEOGTI9neImD3AwzCB/WPq2BJCZAWe2R4Q==", "path": "serilog.settings.configuration/2.6.1", "hashPath": "serilog.settings.configuration.2.6.1.nupkg.sha512" }, "Serilog.Sinks.Console/3.1.1": { "type": "package", "serviceable": true, "sha512": "sha512-56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==", "path": "serilog.sinks.console/3.1.1", "hashPath": "serilog.sinks.console.3.1.1.nupkg.sha512" }, "Serilog.Sinks.File/3.2.0": { "type": "package", "serviceable": true, "sha512": "sha512-VHbo68pMg5hwSWrzLEdZv5b/rYmIgHIRhd4d5rl8GnC5/a8Fr+RShT5kWyeJOXax1el6mNJ+dmHDOVgnNUQxaw==", "path": "serilog.sinks.file/3.2.0", "hashPath": "serilog.sinks.file.3.2.0.nupkg.sha512" }, "Serilog.Sinks.RollingFile/3.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-2lT5X1r3GH4P0bRWJfhA7etGl8Q2Ipw9AACvtAHWRUSpYZ42NGVyHoVs2ALBZ/cAkkS+tA4jl80Zie144eLQPg==", "path": "serilog.sinks.rollingfile/3.3.0", "hashPath": "serilog.sinks.rollingfile.3.3.0.nupkg.sha512" }, "Superpower/2.0.0": { "type": "package", "serviceable": true, "sha512": "sha512-LvsFRxO7VJuUXiaIr3agLuTQTCVYtzgmGJ4BiNe4/DKplOMZ4pibpVSg1ON5zXdCci1jYNfRgQlNEKv0d0Kcog==", "path": "superpower/2.0.0", "hashPath": "superpower.2.0.0.nupkg.sha512" }, "System.AppContext/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", "path": "system.appcontext/4.3.0", "hashPath": "system.appcontext.4.3.0.nupkg.sha512" }, "System.Buffers/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-xpHYjjtyTEpzMwtSQBWdVc3dPjLdQtvyUg6fBlBqcLl1r2Y7gDG/W/enAYOB98nG3oD3Q153Y2FBO8JDWd+0Xw==", "path": "system.buffers/4.5.0", "hashPath": "system.buffers.4.5.0.nupkg.sha512" }, "System.Collections/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", "path": "system.collections/4.3.0", "hashPath": "system.collections.4.3.0.nupkg.sha512" }, "System.Collections.Concurrent/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", "path": "system.collections.concurrent/4.3.0", "hashPath": "system.collections.concurrent.4.3.0.nupkg.sha512" }, "System.Collections.Immutable/1.3.1": { "type": "package", "serviceable": true, "sha512": "sha512-n+AGX7zmiZumW9aggOkXaHzUeAS3EfeTErnkKCusyONUozbTv+kMb8VE36m+ldV6kF9g57G2c641KCdgH9E0pg==", "path": "system.collections.immutable/1.3.1", "hashPath": "system.collections.immutable.1.3.1.nupkg.sha512" }, "System.Collections.NonGeneric/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", "path": "system.collections.nongeneric/4.3.0", "hashPath": "system.collections.nongeneric.4.3.0.nupkg.sha512" }, "System.ComponentModel/4.0.1": { "type": "package", "serviceable": true, "sha512": "sha512-oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==", "path": "system.componentmodel/4.0.1", "hashPath": "system.componentmodel.4.0.1.nupkg.sha512" }, "System.ComponentModel.Annotations/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-IjDa643EO77A4CL9dhxfZ6zzGu+pM8Ar0NYPRMN3TvDiga4uGDzFHOj/ArpyNxxKyO5IFT2LZ0rK3kUog7g3jA==", "path": "system.componentmodel.annotations/4.5.0", "hashPath": "system.componentmodel.annotations.4.5.0.nupkg.sha512" }, "System.Console/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", "path": "system.console/4.3.0", "hashPath": "system.console.4.3.0.nupkg.sha512" }, "System.Diagnostics.Debug/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", "path": "system.diagnostics.debug/4.3.0", "hashPath": "system.diagnostics.debug.4.3.0.nupkg.sha512" }, "System.Diagnostics.DiagnosticSource/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-UumL3CJklk5WyEt0eImPmjeuyY1JgJ7Thmg2hAeZGKCv+9iuuAsoc2wcXjypdo3J8VNEmVCH2Bgn/kIw8NI2bA==", "path": "system.diagnostics.diagnosticsource/4.5.0", "hashPath": "system.diagnostics.diagnosticsource.4.5.0.nupkg.sha512" }, "System.Diagnostics.FileVersionInfo/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-omCF64wzQ3Q2CeIqkD6lmmxeMZtGHUmzgFMPjfVaOsyqpR66p/JaZzManMw1s33osoAb5gqpncsjie67+yUPHQ==", "path": "system.diagnostics.fileversioninfo/4.3.0", "hashPath": "system.diagnostics.fileversioninfo.4.3.0.nupkg.sha512" }, "System.Diagnostics.StackTrace/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-BiHg0vgtd35/DM9jvtaC1eKRpWZxr0gcQd643ABG7GnvSlf5pOkY2uyd42mMOJoOmKvnpNj0F4tuoS1pacTwYw==", "path": "system.diagnostics.stacktrace/4.3.0", "hashPath": "system.diagnostics.stacktrace.4.3.0.nupkg.sha512" }, "System.Diagnostics.Tools/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", "path": "system.diagnostics.tools/4.3.0", "hashPath": "system.diagnostics.tools.4.3.0.nupkg.sha512" }, "System.Diagnostics.Tracing/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", "path": "system.diagnostics.tracing/4.3.0", "hashPath": "system.diagnostics.tracing.4.3.0.nupkg.sha512" }, "System.Dynamic.Runtime/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", "path": "system.dynamic.runtime/4.3.0", "hashPath": "system.dynamic.runtime.4.3.0.nupkg.sha512" }, "System.Globalization/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", "path": "system.globalization/4.3.0", "hashPath": "system.globalization.4.3.0.nupkg.sha512" }, "System.Globalization.Calendars/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", "path": "system.globalization.calendars/4.3.0", "hashPath": "system.globalization.calendars.4.3.0.nupkg.sha512" }, "System.Globalization.Extensions/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", "path": "system.globalization.extensions/4.3.0", "hashPath": "system.globalization.extensions.4.3.0.nupkg.sha512" }, "System.IO/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", "path": "system.io/4.3.0", "hashPath": "system.io.4.3.0.nupkg.sha512" }, "System.IO.Compression/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", "path": "system.io.compression/4.3.0", "hashPath": "system.io.compression.4.3.0.nupkg.sha512" }, "System.IO.FileSystem/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", "path": "system.io.filesystem/4.3.0", "hashPath": "system.io.filesystem.4.3.0.nupkg.sha512" }, "System.IO.FileSystem.Primitives/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", "path": "system.io.filesystem.primitives/4.3.0", "hashPath": "system.io.filesystem.primitives.4.3.0.nupkg.sha512" }, "System.IO.Pipelines/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-Kq9eZWVKN9khHhkatLWLLxYCs3j9qSNMZELqn2YG1YsCMv6bPmAtaN0CfA6l7vxFbiV02C996Dy7yHO8DkaJLg==", "path": "system.io.pipelines/4.5.0", "hashPath": "system.io.pipelines.4.5.0.nupkg.sha512" }, "System.Linq/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", "path": "system.linq/4.3.0", "hashPath": "system.linq.4.3.0.nupkg.sha512" }, "System.Linq.Expressions/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", "path": "system.linq.expressions/4.3.0", "hashPath": "system.linq.expressions.4.3.0.nupkg.sha512" }, "System.Memory/4.5.1": { "type": "package", "serviceable": true, "sha512": "sha512-vcG3/MbfpxznMkkkaAblJi7RHOmuP7kawQMhDgLSuA1tRpRQYsFSCTxRSINDUgn2QNn2jWeLxv8er5BXbyACkw==", "path": "system.memory/4.5.1", "hashPath": "system.memory.4.5.1.nupkg.sha512" }, "System.Net.Http/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", "path": "system.net.http/4.3.0", "hashPath": "system.net.http.4.3.0.nupkg.sha512" }, "System.Net.Primitives/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", "path": "system.net.primitives/4.3.0", "hashPath": "system.net.primitives.4.3.0.nupkg.sha512" }, "System.Net.Requests/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-OZNUuAs0kDXUzm7U5NZ1ojVta5YFZmgT2yxBqsQ7Eseq5Ahz88LInGRuNLJ/NP2F8W1q7tse1pKDthj3reF5QA==", "path": "system.net.requests/4.3.0", "hashPath": "system.net.requests.4.3.0.nupkg.sha512" }, "System.Net.WebHeaderCollection/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-XZrXYG3c7QV/GpWeoaRC02rM6LH2JJetfVYskf35wdC/w2fFDFMphec4gmVH2dkll6abtW14u9Rt96pxd9YH2A==", "path": "system.net.webheadercollection/4.3.0", "hashPath": "system.net.webheadercollection.4.3.0.nupkg.sha512" }, "System.Numerics.Vectors/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-MNcaYxUJvUcoXOa+jgKl/GDw/Mh+wMrxDjW4dre7qrp35LUGTjUBNtZsNjxsWX592ocdyqt1X5hMJB+5OStoYw==", "path": "system.numerics.vectors/4.5.0", "hashPath": "system.numerics.vectors.4.5.0.nupkg.sha512" }, "System.ObjectModel/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", "path": "system.objectmodel/4.3.0", "hashPath": "system.objectmodel.4.3.0.nupkg.sha512" }, "System.Reflection/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", "path": "system.reflection/4.3.0", "hashPath": "system.reflection.4.3.0.nupkg.sha512" }, "System.Reflection.Emit/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", "path": "system.reflection.emit/4.3.0", "hashPath": "system.reflection.emit.4.3.0.nupkg.sha512" }, "System.Reflection.Emit.ILGeneration/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", "path": "system.reflection.emit.ilgeneration/4.3.0", "hashPath": "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512" }, "System.Reflection.Emit.Lightweight/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", "path": "system.reflection.emit.lightweight/4.3.0", "hashPath": "system.reflection.emit.lightweight.4.3.0.nupkg.sha512" }, "System.Reflection.Extensions/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", "path": "system.reflection.extensions/4.3.0", "hashPath": "system.reflection.extensions.4.3.0.nupkg.sha512" }, "System.Reflection.Metadata/1.6.0": { "type": "package", "serviceable": true, "sha512": "sha512-I4aWCii7N1bmn43vviRfJQYW6UAco1G/CcjJouvgGdb/sr2BRTSnddhaPMg2oxu9VHFn8T1z3dTLq0pna8zmtA==", "path": "system.reflection.metadata/1.6.0", "hashPath": "system.reflection.metadata.1.6.0.nupkg.sha512" }, "System.Reflection.Primitives/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", "path": "system.reflection.primitives/4.3.0", "hashPath": "system.reflection.primitives.4.3.0.nupkg.sha512" }, "System.Reflection.TypeExtensions/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", "path": "system.reflection.typeextensions/4.3.0", "hashPath": "system.reflection.typeextensions.4.3.0.nupkg.sha512" }, "System.Resources.ResourceManager/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", "path": "system.resources.resourcemanager/4.3.0", "hashPath": "system.resources.resourcemanager.4.3.0.nupkg.sha512" }, "System.Runtime/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", "path": "system.runtime/4.3.0", "hashPath": "system.runtime.4.3.0.nupkg.sha512" }, "System.Runtime.CompilerServices.Unsafe/4.5.1": { "type": "package", "serviceable": true, "sha512": "sha512-qUJMNWhbm9oZ3XaMFiEMiYmRPszbnXIkRIi7+4b2Md2xZ6JUOepf0/kY3S85qistRohl9OdMe4PsO+RdG2kTIQ==", "path": "system.runtime.compilerservices.unsafe/4.5.1", "hashPath": "system.runtime.compilerservices.unsafe.4.5.1.nupkg.sha512" }, "System.Runtime.Extensions/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", "path": "system.runtime.extensions/4.3.0", "hashPath": "system.runtime.extensions.4.3.0.nupkg.sha512" }, "System.Runtime.Handles/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", "path": "system.runtime.handles/4.3.0", "hashPath": "system.runtime.handles.4.3.0.nupkg.sha512" }, "System.Runtime.InteropServices/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", "path": "system.runtime.interopservices/4.3.0", "hashPath": "system.runtime.interopservices.4.3.0.nupkg.sha512" }, "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", "path": "system.runtime.interopservices.runtimeinformation/4.3.0", "hashPath": "system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512" }, "System.Runtime.Numerics/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", "path": "system.runtime.numerics/4.3.0", "hashPath": "system.runtime.numerics.4.3.0.nupkg.sha512" }, "System.Security.AccessControl/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-aVjTe36YkO8FzfNhMLoPEzv3gF9rphoW9ngFhG/MH4zzEPLx07sNrgCLwMP4Wx2leI6qarMrGv21OwQXYUKLmw==", "path": "system.security.accesscontrol/4.5.0", "hashPath": "system.security.accesscontrol.4.5.0.nupkg.sha512" }, "System.Security.Cryptography.Algorithms/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", "path": "system.security.cryptography.algorithms/4.3.0", "hashPath": "system.security.cryptography.algorithms.4.3.0.nupkg.sha512" }, "System.Security.Cryptography.Cng/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-O4tqXxWCD8y1IU1VTgzbuBFwoRahrADhDUxHjwezhHCsqyFNyQ5EytjWBxu0EsZuH14b4UO2pFkG063K2h/9Ug==", "path": "system.security.cryptography.cng/4.5.0", "hashPath": "system.security.cryptography.cng.4.5.0.nupkg.sha512" }, "System.Security.Cryptography.Csp/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", "path": "system.security.cryptography.csp/4.3.0", "hashPath": "system.security.cryptography.csp.4.3.0.nupkg.sha512" }, "System.Security.Cryptography.Encoding/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", "path": "system.security.cryptography.encoding/4.3.0", "hashPath": "system.security.cryptography.encoding.4.3.0.nupkg.sha512" }, "System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", "path": "system.security.cryptography.openssl/4.3.0", "hashPath": "system.security.cryptography.openssl.4.3.0.nupkg.sha512" }, "System.Security.Cryptography.Pkcs/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-1vv2x8cok3NAolee/nb6X/6PnTx+OBKUM3kt1Rlgg04uQ+IMwjc88xFIfJdwbYcvjlOtzT7CHba1pqVAu9tj/w==", "path": "system.security.cryptography.pkcs/4.5.0", "hashPath": "system.security.cryptography.pkcs.4.5.0.nupkg.sha512" }, "System.Security.Cryptography.Primitives/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", "path": "system.security.cryptography.primitives/4.3.0", "hashPath": "system.security.cryptography.primitives.4.3.0.nupkg.sha512" }, "System.Security.Cryptography.X509Certificates/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", "path": "system.security.cryptography.x509certificates/4.3.0", "hashPath": "system.security.cryptography.x509certificates.4.3.0.nupkg.sha512" }, "System.Security.Cryptography.Xml/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-UvxfrEg7YG7U6BQO8WdQ4Nu1LFt2lqYQnoZefaK/2RDvjYdJ+norsVe4dwOqo14XiipgYY5xNUo6VhQXNbl2vg==", "path": "system.security.cryptography.xml/4.5.0", "hashPath": "system.security.cryptography.xml.4.5.0.nupkg.sha512" }, "System.Security.Permissions/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-vDQ7q30Soe0a1cPhvxn+7IFmMeTG5IP+hTQrnKQDjTNpD2epqwbZSzMM2Git5TXBr4Kwwhc/0SEtJY0qPoiegA==", "path": "system.security.permissions/4.5.0", "hashPath": "system.security.permissions.4.5.0.nupkg.sha512" }, "System.Security.Principal.Windows/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-WA9ETb/pY3BjnxKjBUHEgO59B7d/nnmjHFsqjJ2eDT780nD769CT1/bw2ia0Z6W7NqlcqokE6sKGKa6uw88XGA==", "path": "system.security.principal.windows/4.5.0", "hashPath": "system.security.principal.windows.4.5.0.nupkg.sha512" }, "System.Text.Encoding/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", "path": "system.text.encoding/4.3.0", "hashPath": "system.text.encoding.4.3.0.nupkg.sha512" }, "System.Text.Encoding.CodePages/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-IRiEFUa5b/Gs5Egg8oqBVoywhtOeaO2KOx3j0RfcYY/raxqBuEK7NXRDgOwtYM8qbi+7S4RPXUbNt+ZxyY0/NQ==", "path": "system.text.encoding.codepages/4.3.0", "hashPath": "system.text.encoding.codepages.4.3.0.nupkg.sha512" }, "System.Text.Encoding.Extensions/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", "path": "system.text.encoding.extensions/4.3.0", "hashPath": "system.text.encoding.extensions.4.3.0.nupkg.sha512" }, "System.Text.Encodings.Web/4.5.0": { "type": "package", "serviceable": true, "sha512": "sha512-JF+wDdfFiRl3rz3dPMfR6aR568AW2J5CUMmhSflgHDz4zbVK4/00ax8UHnHyEMvblPewgNugjuA4oyoL8Pex2g==", "path": "system.text.encodings.web/4.5.0", "hashPath": "system.text.encodings.web.4.5.0.nupkg.sha512" }, "System.Text.RegularExpressions/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", "path": "system.text.regularexpressions/4.3.0", "hashPath": "system.text.regularexpressions.4.3.0.nupkg.sha512" }, "System.Threading/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", "path": "system.threading/4.3.0", "hashPath": "system.threading.4.3.0.nupkg.sha512" }, "System.Threading.Tasks/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", "path": "system.threading.tasks/4.3.0", "hashPath": "system.threading.tasks.4.3.0.nupkg.sha512" }, "System.Threading.Tasks.Extensions/4.5.1": { "type": "package", "serviceable": true, "sha512": "sha512-rckdhLJtzQ3EI+0BGuq7dUVtCSnerqAoAmL3S6oMRZ4VMZTL3Rq9DS8IDW57c6PYVebA4O0NbSA1BDvyE18UMA==", "path": "system.threading.tasks.extensions/4.5.1", "hashPath": "system.threading.tasks.extensions.4.5.1.nupkg.sha512" }, "System.Threading.Tasks.Parallel/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-cbjBNZHf/vQCfcdhzx7knsiygoCKgxL8mZOeocXZn5gWhCdzHIq6bYNKWX0LAJCWYP7bds4yBK8p06YkP0oa0g==", "path": "system.threading.tasks.parallel/4.3.0", "hashPath": "system.threading.tasks.parallel.4.3.0.nupkg.sha512" }, "System.Threading.Thread/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-OHmbT+Zz065NKII/ZHcH9XO1dEuLGI1L2k7uYss+9C1jLxTC9kTZZuzUOyXHayRk+dft9CiDf3I/QZ0t8JKyBQ==", "path": "system.threading.thread/4.3.0", "hashPath": "system.threading.thread.4.3.0.nupkg.sha512" }, "System.Threading.Timer/4.0.1": { "type": "package", "serviceable": true, "sha512": "sha512-saGfUV8uqVW6LeURiqxcGhZ24PzuRNaUBtbhVeuUAvky1naH395A/1nY0P2bWvrw/BreRtIB/EzTDkGBpqCwEw==", "path": "system.threading.timer/4.0.1", "hashPath": "system.threading.timer.4.0.1.nupkg.sha512" }, "System.ValueTuple/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-cNLEvBX3d6MMQRZe3SMFNukVbitDAEpVZO17qa0/2FHxZ7Y7PpFRpr6m2615XYM/tYYYf0B+WyHNujqIw8Luwg==", "path": "system.valuetuple/4.3.0", "hashPath": "system.valuetuple.4.3.0.nupkg.sha512" }, "System.Xml.ReaderWriter/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", "path": "system.xml.readerwriter/4.3.0", "hashPath": "system.xml.readerwriter.4.3.0.nupkg.sha512" }, "System.Xml.XDocument/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", "path": "system.xml.xdocument/4.3.0", "hashPath": "system.xml.xdocument.4.3.0.nupkg.sha512" }, "System.Xml.XmlDocument/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==", "path": "system.xml.xmldocument/4.3.0", "hashPath": "system.xml.xmldocument.4.3.0.nupkg.sha512" }, "System.Xml.XPath/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-v1JQ5SETnQusqmS3RwStF7vwQ3L02imIzl++sewmt23VGygix04pEH+FCj1yWb+z4GDzKiljr1W7Wfvrx0YwgA==", "path": "system.xml.xpath/4.3.0", "hashPath": "system.xml.xpath.4.3.0.nupkg.sha512" }, "System.Xml.XPath.XDocument/4.3.0": { "type": "package", "serviceable": true, "sha512": "sha512-jw9oHHEIVW53mHY9PgrQa98Xo2IZ0ZjrpdOTmtvk+Rvg4tq7dydmxdNqUvJ5YwjDqhn75mBXWttWjiKhWP53LQ==", "path": "system.xml.xpath.xdocument/4.3.0", "hashPath": "system.xml.xpath.xdocument.4.3.0.nupkg.sha512" }, "Telegram.Bot/14.6.0": { "type": "package", "serviceable": true, "sha512": "sha512-ZEHe1AgqMGLv8WkxeKpnBe5wgkr4AscH7Jrix2hUFRfltCo3TkSh6g6P7/m2MacMsQ/kVd2Ah8/oOe+QrVBJhA==", "path": "telegram.bot/14.6.0", "hashPath": "telegram.bot.14.6.0.nupkg.sha512" }, "IntelliTrader.Core/1.0.0": { "type": "project", "serviceable": false, "sha512": "" }, "Microsoft.NETCore.App/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-AvT774nTFgU8cYcGO9j1EMwuayKslxqYTurg32HGpWa2hEYNuW2+XgYVVNcZe6Ndbr84QX6fwaOZfd5n+1m2OA==", "path": "microsoft.netcore.app/2.1.0", "hashPath": "microsoft.netcore.app.2.1.0.nupkg.sha512" }, "Microsoft.NETCore.DotNetAppHost/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-f/47I60Wg3SrveTvnecCQhCZCAMYlUujWF15EQ/AZTqF/54qeEJjbCIAxKcZI8ToUYzSg6JdfrHggsgjCyCE9Q==", "path": "microsoft.netcore.dotnetapphost/2.1.0", "hashPath": "microsoft.netcore.dotnetapphost.2.1.0.nupkg.sha512" }, "Microsoft.NETCore.DotNetHostPolicy/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-p50yZYKzhH64lmArJgoKjtvsNehECa+/sAuOQzZh5uDNBTbRKxjN8IXP1e517xdVsgrFcSNxSEVDKZIOWVjGcQ==", "path": "microsoft.netcore.dotnethostpolicy/2.1.0", "hashPath": "microsoft.netcore.dotnethostpolicy.2.1.0.nupkg.sha512" }, "Microsoft.NETCore.DotNetHostResolver/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-fS9D8a+y55n6mHMbNqgHXaPGkjmpVH9h97OyrBxsCuo3Z8aQaFMJ5xIfmzji2ntUd/3truhMbSgSfIelHOkQpg==", "path": "microsoft.netcore.dotnethostresolver/2.1.0", "hashPath": "microsoft.netcore.dotnethostresolver.2.1.0.nupkg.sha512" }, "Microsoft.NETCore.Platforms/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-TT+QCi9LcxGTjBssH7S7n5+8DVcwfG4DYgXX7Dk7+BfZ4oVHj8Q0CbYk9glzAlHLsSt3bYzol+fOdra2iu6GOw==", "path": "microsoft.netcore.platforms/2.1.0", "hashPath": "microsoft.netcore.platforms.2.1.0.nupkg.sha512" }, "Microsoft.NETCore.Targets/2.1.0": { "type": "package", "serviceable": true, "sha512": "sha512-etaYwrLZQUS+b3UWTpCnUggd6SQ/ZIkZ5pHnoR7+dIWt/wp2Rv3CvMKOZISsrt7FYCHKwCxfcepuuyEWkQxADg==", "path": "microsoft.netcore.targets/2.1.0", "hashPath": "microsoft.netcore.targets.2.1.0.nupkg.sha512" }, "NETStandard.Library/2.0.3": { "type": "package", "serviceable": true, "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", "path": "netstandard.library/2.0.3", "hashPath": "netstandard.library.2.0.3.nupkg.sha512" } } } ================================================ FILE: IntelliTrader/IntelliTrader.csproj ================================================ Exe netcoreapp2.1 Never Never Never Never Never Never PreserveNewest Never ================================================ FILE: IntelliTrader/IntelliTrader.sh ================================================ #!/bin/bash dotnet bin/IntelliTrader.dll ================================================ FILE: IntelliTrader/Program.cs ================================================ using Autofac; using ExchangeSharp; using IntelliTrader.Core; using System; using System.Collections.Generic; namespace IntelliTrader { class Program { static void Main(string[] args) { var parsedArgs = ParseCommandLineArgs(args); if (parsedArgs.Count == 0) { PringWelcome(); StartCoreService(); } else { if (parsedArgs.ContainsKey("encrypt") && parsedArgs.ContainsKey("path") && parsedArgs.ContainsKey("publickey") && parsedArgs.ContainsKey("privatekey")) { EncryptKeys(parsedArgs); } else { PrintUsage(); } } } private static void StartCoreService() { var coreService = Application.Resolve(); coreService.Start(); Console.ReadLine(); coreService.Stop(); } private static void PringWelcome() { var foregroundColorBackup = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(); Console.WriteLine(@" _____ _ _ _ _ _____ _ "); Console.WriteLine(@" \_ \ _ __ | |_ ___ | || |(_)/__ \ _ __ __ _ __| | ___ _ __ "); Console.WriteLine(@" / /\/| '_ \ | __| / _ \| || || | / /\/| '__| / _` | / _` | / _ \| '__|"); Console.WriteLine(@"/\/ /_ | | | || |_ | __/| || || | / / | | | (_| || (_| || __/| | "); Console.WriteLine(@"\____/ |_| |_| \__| \___||_||_||_| \/ |_| \__,_| \__,_| \___||_| "); Console.WriteLine(); Console.WriteLine("Welcome to IntelliTrader, The Intelligent Cryptocurrency Trading Bot."); Console.WriteLine("Always use Enter/Return key to exit the program to avoid corrupting the data."); Console.WriteLine(); Console.ForegroundColor = foregroundColorBackup; } private static void EncryptKeys(Dictionary args) { var path = args["path"]; var publicKey = args["publickey"]; var privateKey = args["privatekey"]; CryptoUtility.SaveUnprotectedStringsToFile(path, new string[] { publicKey, privateKey }); Console.WriteLine("All done! Press any key to exit..."); Console.ReadKey(); } private static void PrintUsage() { Console.WriteLine(); Console.WriteLine("Usage: dotnet IntelliTrader.dll --encrypt --path= --publickey= --privatekey="); Console.WriteLine("The encrypted file is only valid for the current user and only on the computer it is created on."); Console.WriteLine(); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } private static Dictionary ParseCommandLineArgs(string[] args) { var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (string a in args) { int idx = a.IndexOf('='); string key = (idx < 0 ? a.TrimStart('-') : a.Substring(0, idx)).ToLowerInvariant().TrimStart('-'); string value = (idx < 0 ? string.Empty : a.Substring(idx + 1)); dict[key] = value; } return dict; } } } ================================================ FILE: IntelliTrader/Properties/PublishProfiles/FolderProfile.pubxml ================================================  FileSystem Release netcoreapp2.1 ..\Publish\bin ================================================ FILE: IntelliTrader/config/backtesting.json ================================================ { "Backtesting": { "Enabled": false, "Replay": false, "ReplayOutput": true, "ReplaySpeed": 50, "ReplayStartIndex": null, "ReplayEndIndex": null, "DeleteLogs": true, "DeleteAccountData": true, "CopyAccountDataPath": null, "TradingSpeedEasing": 0, "TradingRulesSpeedEasing": 0, "SignalRulesSpeedEasing": 0, "SnapshotsInterval": 1, "SnapshotsPath": "data/backtesting" } } ================================================ FILE: IntelliTrader/config/core.json ================================================ { "Core": { "DebugMode": false, "PasswordProtected": true, "Password": "b84967c4f073b71405404f3719c788cd", "InstanceName": "Main", "TimezoneOffset": 1, "HealthCheckEnabled": true, "HealthCheckInterval": 180, "HealthCheckSuspendTradingTimeout": 900, "HealthCheckFailuresToRestartServices": 5 } } ================================================ FILE: IntelliTrader/config/exchange.json ================================================ { "Exchange": { "KeysPath": "data/keys.bin", "RateLimitOccurences": 40, "RateLimitTimeframe": 10 } } ================================================ FILE: IntelliTrader/config/logging.json ================================================ { "Logging": { "Enabled": true, "MinimumLevel": { "Default": "Verbose", "Override": { "System": "Warning", "Microsoft": "Warning" } }, "WriteTo": [ { "Name": "Logger", "Args": { "configureLogger": { "WriteTo": [ { "Name": "Console", "Args": { "outputTemplate": "{Timestamp:HH:mm:ss} [{Level:u3}] {Message}{NewLine}{Exception}", "theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Literate, Serilog.Sinks.Console", "restrictedToMinimumLevel": "Information" } }, { "Name": "RollingFile", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] {Message}{NewLine}{Exception}", "pathFormat": "log/{Date}-general.txt", "retainedFileCountLimit": 1000 } } ], "Filter": [ { "Name": "ByIncludingOnly", "Args": { "expression": "Trade is null" } } ] } } }, { "Name": "Logger", "Args": { "configureLogger": { "WriteTo": [ { "Name": "RollingFile", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] {Message}{NewLine}", "pathFormat": "log/{Date}-trades.txt", "retainedFileCountLimit": 1000 } } ], "Filter": [ { "Name": "ByIncludingOnly", "Args": { "expression": "Trade is not null" } } ] } } } ] } } ================================================ FILE: IntelliTrader/config/notification.json ================================================ { "Notification": { "Enabled": false, "TelegramEnabled": true, "TelegramBotToken": "", "TelegramChatId": 0, "TelegramAlertsEnabled": true } } ================================================ FILE: IntelliTrader/config/paths.json ================================================ { "Paths": { "Core": "core.json", "Logging": "logging.json", "Trading": "trading.json", "Exchange": "exchange.json", "Signals": "signals.json", "Rules": "rules.json", "Notification": "notification.json", "Web": "web.json", "Backtesting": "backtesting.json" } } ================================================ FILE: IntelliTrader/config/rules.json ================================================ { "Rules": { "Modules": [ { "Module": "Signals", "Configuration": { "ProcessingMode": "AllMatches", "CheckInterval": 0.1 }, "Entries": [ { "Enabled": false, "Name": "Buy-Arbitrage", "Action": "Arbitrage", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "MinArbitrage": 4 } ] }, { "Enabled": true, "Name": "Buy-Safe", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinVolatility": 2.5, "MaxVolatility": 10, "MaxPriceChange": 5 }, { "Signal": "TV-15m", "MinRating": 0.30, "MaxPriceChange": 5 }, { "Signal": "TV-1h", "MinRating": 0.25, "MinVolume": 100000, "MaxPriceChange": 6 }, { "Signal": "TV-4h", "MinRating": 0.1, "MinPriceChange": 1.5, "MaxPriceChange": 12 }, { "Signal": "TV-1d", "MinPriceChange": 5, "MaxPriceChange": 20 }, { "MinGlobalRating": -0.35, "MaxGlobalRating": 1.0, "MaxSpread": 0.25, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Buy-Bull", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinVolatility": 3, "MaxVolatility": 12, "MaxPriceChange": 5 }, { "Signal": "TV-15m", "MinRating": 0.30, "MaxPriceChange": 5 }, { "Signal": "TV-1h", "MinRating": 0.25, "MinVolume": 100000, "MaxPriceChange": 8 }, { "Signal": "TV-4h", "MinRating": 0.1, "MinPriceChange": 2, "MaxPriceChange": 12 }, { "Signal": "TV-1d", "MinPriceChange": 4, "MaxPriceChange": 20 }, { "MinGlobalRating": 0.25, "MaxSpread": 0.35, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": false, "Name": "Buy-TUSDT", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinRating": 0.30 }, { "Signal": "TV-5m", "MinRating": 0.30 }, { "Signal": "TV-15m", "MinRating": 0.30 }, { "MinGlobalRating": -0.28, "MaxGlobalRating": -0.15, "MaxSpread": 0.35, "Pairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Buy-Volume-Spike", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-5m", "MinRating": 0.4, "MinPriceChange": 1, "MaxPriceChange": 8, "MinVolumeChange": 500, "MaxVolatility": 12 }, { "Signal": "TV-15m", "MinRating": 0.4, "MinPriceChange": 1.5, "MaxPriceChange": 9, "MinVolumeChange": 200 }, { "Signal": "TV-1h", "MinRating": 0.25, "MinVolume": 200000, "MaxPriceChange": 8 }, { "Signal": "TV-4h", "MinPriceChange": 1, "MaxPriceChange": 10 }, { "Signal": "TV-1d", "MinPriceChange": 2, "MaxPriceChange": 20 }, { "MinGlobalRating": -0.10, "MaxGlobalRating": 1.0, "MaxSpread": 1, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Buy-Pump", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinVolumeChange": 20, "MaxVolumeChange": 200, "MinRating": 0.30 }, { "Signal": "TV-5m", "MinRating": 0.30, "MinPriceChange": 3, "MinVolumeChange": 0, "MaxPriceChange": 10 }, { "Signal": "TV-15m", "MinRating": 0.30 }, { "Signal": "TV-1h", "MinRating": 0.0, "MinVolume": 100000, "MaxPriceChange": 8 }, { "Signal": "TV-4h", "MinPriceChange": 3, "MaxPriceChange": 10 }, { "Signal": "TV-1d", "MinPriceChange": 4, "MaxPriceChange": 20 }, { "MinGlobalRating": -0.30, "MaxGlobalRating": 1.0, "MaxSpread": 0.6 } ], "Trailing": { "Enabled": true, "MinDuration": 10, "MaxDuration": 60, "StartConditions": [ { "Signal": "TV-5m", "MinVolumeChange": 200 } ] } }, { "Enabled": true, "Name": "Swap-Safe", "Action": "Swap", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-15m", "MinRating": 0.20, "MaxRating": 0.35, "MaxPriceChange": 5 }, { "Signal": "TV-1h", "MinRating": 0.10, "MaxRating": 0.25, "MinVolume": 400000, "MinRatingChange": 0, "MaxPriceChange": 6 }, { "Signal": "TV-4h", "MinRating": 0.10, "MaxRating": 0.25, "MinPriceChange": 1, "MaxPriceChange": 8 }, { "Signal": "TV-1d", "MinRatingChange": 0, "MinPriceChange": 2, "MaxPriceChange": 12 }, { "MinGlobalRating": -0.20, "MaxGlobalRating": 1, "MaxSpread": 0.35, "NotPairs": [ "TUSDBTC" ] } ] } ] }, { "Module": "Trading", "Configuration": { "ProcessingMode": "AllMatches", "CheckInterval": 0.1 }, "Entries": [ { "Enabled": true, "Name": "TUSD-DCA", "Modifiers": { "BuyEnabled": true, "BuyDCAEnabled": true, "SellMargin": 0.20, "SellTrailing": 0.15, "DCALevels": [ { "Margin": -0.40, "BuySamePairTimeout": 0, "BuyTrailing": -0.20, "BuyTrailingStopMargin": 1.00, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.20, "SellTrailing": 0.15 }, { "Margin": -1.25, "BuySamePairTimeout": 180, "BuyTrailing": -0.20, "BuyTrailingStopMargin": 1.50, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.15, "SellTrailing": 0.15 }, { "Margin": -2.50, "BuySamePairTimeout": 300, "BuyTrailing": -0.25, "BuyTrailingStopMargin": 2.00, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.10, "SellTrailing": 0.10 }, { "Margin": -5.50, "BuySamePairTimeout": 1800, "BuyTrailing": -0.25, "BuyTrailingStopMargin": 2.00, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.05, "SellTrailing": 0.10 } ] }, "Conditions": [ { "MinGlobalRating": -1.00, "MaxGlobalRating": -0.10, "Pairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Apocalypse", "Modifiers": { "BuyEnabled": false, "BuyDCAEnabled": false, "SellMargin": -0.50, "SellTrailing": 0, "SellDCAMargin": -0.50, "SellDCATrailing": 0 }, "Conditions": [ { "MinGlobalRating": -1, "MaxGlobalRating": -0.40 } ] }, { "Enabled": true, "Name": "Bear", "Modifiers": { "BuyDCAEnabled": false, "BuyTrailing": -0.45, "SellMargin": 0.40, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.30, "MaxPairs": 4, "DCALevels": [ { "Margin": -4, "SellMargin": 0.20, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.35, "BuySamePairTimeout": 0 }, { "Margin": -8, "SellMargin": 0.15, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.10, "BuyTrailing": -0.55, "BuySamePairTimeout": 600 }, { "Margin": -15, "SellMargin": 0.10, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.10, "BuyTrailing": -1, "BuySamePairTimeout": 1200 }, { "Margin": -20, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": -1.5, "BuySamePairTimeout": 2880 } ] }, "Conditions": [ { "MinGlobalRating": -0.40, "MaxGlobalRating": -0.15, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "BuyDCAEnabled": false, "Name": "Boring", "Modifiers": { "BuyTrailing": -0.35, "SellMargin": 0.50, "SellTrailing": 0.35, "SellTrailingStopMargin": 0.40, "MaxPairs": 5, "DCALevels": [ { "Margin": -2.5, "SellMargin": 0.30, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.30, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -5, "SellMargin": 0.20, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.45, "BuySamePairTimeout": 300 }, { "Margin": -8, "SellMargin": 0.10, "SellTrailing": 0.10, "SellTrailingStopMargin": 0.10, "BuyTrailing": -0.75, "BuySamePairTimeout": 900 }, { "Margin": -12, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": -1.5, "BuySamePairTimeout": 1800 } ] }, "Conditions": [ { "MinGlobalRating": -0.15, "MaxGlobalRating": 0.15, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Bull", "Modifiers": { "BuyTrailing": -0.25, "SellMargin": 0.60, "SellTrailing": 0.45, "SellTrailingStopMargin": 0.50, "MaxPairs": 6, "DCALevels": [ { "Margin": -0.35, "SellMargin": 0.40, "SellTrailing": 0.35, "SellTrailingStopMargin": 0.35, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -2, "SellMargin": 0.35, "SellTrailing": 0.30, "SellTrailingStopMargin": 0.30, "BuyTrailing": -0.35, "BuySamePairTimeout": 180 }, { "Margin": -7, "SellMargin": 0.15, "SellTrailing": 0.15, "SellTrailingStopMargin": 0.15, "BuyTrailing": -0.55, "BuySamePairTimeout": 600 }, { "Margin": -12, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": -1.5, "BuySamePairTimeout": 1400 } ] }, "Conditions": [ { "MinGlobalRating": 0.15, "MaxGlobalRating": 1.00 } ] }, { "Enabled": true, "Name": "DCA", "Modifiers": { "BuyDCAEnabled": false }, "Conditions": [ { "Signal": "TV-15m", "MaxRating": 0.30 }, { "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "SuperBull-HP-DCA", "Modifiers": { "DCALevels": [ { "Margin": -0.25, "SellMargin": 0.25, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.25, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -0.75, "SellMargin": 0.20, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.45, "BuySamePairTimeout": 300 }, { "Margin": -1.50, "SellMargin": 0.15, "SellTrailing": 0.15, "SellTrailingStopMargin": 0.15, "BuyTrailing": -0.65, "BuySamePairTimeout": 600 }, { "Margin": -4.0, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": 2.0, "BuySamePairTimeout": 1200 } ] }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0.40 }, { "Signal": "TV-15m", "MaxRating": 0.40 }, { "Signal": "TV-1h", "MaxRating": 0.40 }, { "MinGlobalRating": 0.30 } ] }, { "Enabled": true, "Name": "Bull-HP-DCA", "Modifiers": { "DCALevels": [ { "Margin": -0.35, "SellMargin": 0.25, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.25, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -1.5, "SellMargin": 0.20, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.35, "BuySamePairTimeout": 180 }, { "Margin": -5.5, "SellMargin": 0.15, "SellTrailing": 0.15, "SellTrailingStopMargin": 0.15, "BuyTrailing": -0.55, "BuySamePairTimeout": 300 }, { "Margin": -10, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": 2.0, "BuySamePairTimeout": 1200 } ] }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0.40 }, { "Signal": "TV-15m", "MaxRating": 0.40 }, { "Signal": "TV-1h", "MaxRating": 0.30 }, { "MinGlobalRating": 0.20, "MaxGlobalRating": 0.30 } ] }, { "Enabled": true, "Name": "Swap-Only-Bull", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe" ], "SwapTimeout": 1800 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0 }, { "Signal": "TV-15m", "MaxRating": 0 }, { "MaxDCALevel": 2, "MinGlobalRating": 0.30, "MaxGlobalRating": 1.00 } ] }, { "Enabled": true, "Name": "Swap-Money-Pump", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Pump" ], "SwapTimeout": 300 }, "Conditions": [ { "Signal": "TV-15m", "MaxRating": 0 }, { "MaxDCALevel": 0, "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Small-Bag", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Volume-Spike", "Buy-Safe" ], "SwapTimeout": 900 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": -0.30 }, { "Signal": "TV-15m", "MaxRating": -0.30 }, { "Signal": "TV-1h", "MaxRating": 0 }, { "MaxDCALevel": 1, "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Medium-Bag", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe" ], "SwapTimeout": 3600 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": -0.2 }, { "Signal": "TV-15m", "MaxRating": -0.2 }, { "Signal": "TV-1h", "MaxRating": 0 }, { "Signal": "TV-4h", "MaxRating": 0 }, { "MaxDCALevel": 2, "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Big-Bag", "Modifiers": { "BuyDCAEnabled": false, "SwapEnabled": true, "SwapSignalRules": [ "Swap-Safe" ], "SwapTimeout": 7200 }, "Conditions": [ { "Signal": "TV-15m", "MaxRating": -0.2 }, { "Signal": "TV-1h", "MaxRating": 0 }, { "Signal": "TV-4h", "MaxRating": 0 }, { "Signal": "TV-1d", "MaxRating": 0 }, { "MinDCALevel": 3 }, { "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Run-Forest-Run", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe", "Swap-Safe" ], "SwapTimeout": 300 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0 }, { "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "MinMarginChange": 3, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-TUSDT", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe", "Buy-Volume-Spike" ], "SwapTimeout": 300 }, "Conditions": [ { "MinGlobalRating": -0.10, "MaxGlobalRating": 1.00, "Pairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Exclude-BNB", "Modifiers": { "BuyEnabled": false, "BuyDCAEnabled": false, "SellEnabled": false }, "Conditions": [ { "Pairs": [ "BNBBTC" ] } ] }, { "Enabled": true, "Name": "BNB-Top-Up", "Modifiers": { "BuyEnabled": true, "BuyDCAEnabled": true, "BuyDCASamePairTimeout": 0, "BuyMaxCost": 0.003, "RepeatLastDCALevel": true, "DCALevels": [ { "Margin": 100 } ] }, "Conditions": [ { "Pairs": [ "BNBBTC" ], "MaxAmount": 1 } ] }, { "Enabled": true, "Name": "Exclude-Pairs", "Modifiers": { "BuyEnabled": false }, "Conditions": [ { "Pairs": [ "ADABTC", "BCHBTC", "ETHBTC", "XRPBTC", "LTCBTC", "EOSBTC", "XMRBTC", "ZECBTC", "ICXBTC", "DASHBTC", "XLMBTC", "OAXBTC", "RCNBTC", "QSPBTC", "SUBBTC", "BNTBTC", "AEBTC", "TNBBTC", "XEMBTC", "TNTBTC", "TRXBTC", "IOTABTC", "AMBBTC", "DLTBTC", "CDTBTC", "CNDBTC", "CHATBTC", "REQBTC", "CVCBTC", "DNTBTC", "IOTXBTC", "RPXBTC", "VIBBTC" ], "MaxGlobalRating": 0.3 } ] }, { "Enabled": false, "Name": "Arbitrage", "Modifiers": { "BuyEnabled": true, "ArbitrageEnabled": true, "ArbitrageMarkets": [ "ETH", "BNB" ], "ArbitrageBuyMultiplier": 0.985, "ArbitrageSellMultiplier": 0.985, "ArbitrageSignalRules": [ "Buy-Arbitrage" ] }, "Conditions": [ { "MinArbitrage": 3 } ] } ] } ] } } ================================================ FILE: IntelliTrader/config/signals.json ================================================ { "Signals": { "Enabled": true, "GlobalRatingSignals": [ "TV-5m", "TV-15m", "TV-1h" ], "Definitions": [ { "Name": "TV-1m", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 1, "SignalPeriod": 1, "VolatilityPeriod": "Day", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-5m", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 3, "SignalPeriod": 5, "VolatilityPeriod": "Day", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-15m", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 5, "SignalPeriod": 15, "VolatilityPeriod": "Week", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-1h", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 7, "SignalPeriod": 60, "VolatilityPeriod": "Month", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-4h", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 15, "SignalPeriod": 240, "VolatilityPeriod": "Month", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-1d", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 60, "SignalPeriod": 1440, "VolatilityPeriod": "Month", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } } ] } } ================================================ FILE: IntelliTrader/config/trading.json ================================================ { "Trading": { "Enabled": true, "Market": "BTC", "Exchange": "Binance", "MaxPairs": 5, "MinCost": 0.000999, "TradePriceType": "Bid", "ExcludedPairs": [], "BuyEnabled": true, "BuyType": "Market", "BuyMaxCost": 0.03, "BuyMultiplier": 1, "BuyMinBalance": 0, "BuySamePairTimeout": 15, "BuyTrailing": -0.25, "BuyTrailingStopMargin": 1.35, "BuyTrailingStopAction": "Buy", "BuyDCAEnabled": true, "BuyDCAMultiplier": 1, "BuyDCAMinBalance": 0, "BuyDCASamePairTimeout": 180, "BuyDCATrailing": -0.25, "BuyDCATrailingStopMargin": 2, "BuyDCATrailingStopAction": "Buy", "SellEnabled": true, "SellType": "Market", "SellMargin": 1.0, "SellTrailing": 0.65, "SellTrailingStopMargin": 0.7, "SellTrailingStopAction": "Sell", "SellStopLossEnabled": false, "SellStopLossAfterDCA": false, "SellStopLossMinAge": 0, "SellStopLossMargin": -3, "SellDCAMargin": 0.5, "SellDCATrailing": 0.35, "SellDCATrailingStopMargin": 0.5, "SellDCATrailingStopAction": "Sell", "RepeatLastDCALevel": false, "DCALevels": [], "TradingCheckInterval": 0.1, "AccountRefreshInterval": 360, "AccountInitialBalance": 1, "AccountInitialBalanceDate": "2018-04-08T00:00:00+00:00", "AccountFilePath": "data/exchange-account.json", "VirtualTrading": true, "VirtualTradingFees": 0.0005, "VirtualAccountInitialBalance": 1, "VirtualAccountFilePath": "data/virtual-account.json" } } ================================================ FILE: IntelliTrader/config/web.json ================================================ { "Web": { "Enabled": true, "DebugMode": false, "ReadOnlyMode": false, "Port": 7000, "SSLEnabled": false, "SSLCertPath": "data/cert.pfx", "SSLCertPassword": "certpass" } } ================================================ FILE: IntelliTrader/data/encrypt-keys.bat ================================================ dotnet ..\bin\Debug\netcoreapp2.1\IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key pause ================================================ FILE: IntelliTrader/data/encrypt-keys.sh ================================================ #!/bin/bash dotnet bin/IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key ================================================ FILE: IntelliTrader.Backtesting/AppModule.cs ================================================ using Autofac; using IntelliTrader.Core; using System; namespace IntelliTrader.Backtesting { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().As().Named(Constants.ServiceNames.BacktestingService).SingleInstance(); var backtestingConfig = Application.ConfigProvider.GetSection(Constants.ServiceNames.BacktestingService); if (backtestingConfig.Enabled && backtestingConfig.Replay) { builder.RegisterType().Named(Constants.ServiceNames.BacktestingExchangeService).As().Named(Constants.ServiceNames.BacktestingExchangeService).SingleInstance(); builder.RegisterType().As().As().Named(Constants.ServiceNames.BacktestingSignalsService).SingleInstance(); } } } } ================================================ FILE: IntelliTrader.Backtesting/Config/BacktestingConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Backtesting { internal class BacktestingConfig : IBacktestingConfig { public bool Enabled { get; set; } public bool Replay { get; set; } public bool ReplayOutput { get; set; } public double ReplaySpeed { get; set; } public int? ReplayStartIndex { get; set; } public int? ReplayEndIndex { get; set; } public bool DeleteLogs { get; set; } public bool DeleteAccountData { get; set; } public string CopyAccountDataPath { get; set; } public int TradingSpeedEasing { get; set; } public int TradingRulesSpeedEasing { get; set; } public int SignalRulesSpeedEasing { get; set; } public int SnapshotsInterval { get; set; } public string SnapshotsPath { get; set; } } } ================================================ FILE: IntelliTrader.Backtesting/IntelliTrader.Backtesting.csproj ================================================ netcoreapp2.1 ================================================ FILE: IntelliTrader.Backtesting/Model/SignalData.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Signals.Base; using ZeroFormatter; namespace IntelliTrader.Backtesting { [ZeroFormattable] public class SignalData : ISignal { [Index(0)] public virtual string Name { get; set; } [Index(1)] public virtual string Pair { get; set; } [Index(2)] public virtual long? Volume { get; set; } [Index(3)] public virtual double? VolumeChange { get; set; } [Index(4)] public virtual decimal? Price { get; set; } [Index(5)] public virtual decimal? PriceChange { get; set; } [Index(6)] public virtual double? Rating { get; set; } [Index(7)] public virtual double? RatingChange { get; set; } [Index(8)] public virtual double? Volatility { get; set; } public ISignal ToSignal() { return new Signal { Name = Name, Pair = Pair, Volume = Volume, VolumeChange = VolumeChange, Price = Price, PriceChange = PriceChange, Rating = Rating, RatingChange = RatingChange, Volatility = Volatility }; } public static SignalData FromSignal(ISignal signal) { return new SignalData { Name = signal.Name, Pair = signal.Pair, Volume = signal.Volume, VolumeChange = signal.VolumeChange, Price = signal.Price, PriceChange = signal.PriceChange, Rating = signal.Rating, RatingChange = signal.RatingChange, Volatility = signal.Volatility }; } } } ================================================ FILE: IntelliTrader.Backtesting/Model/TickerData.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Exchange.Base; using ZeroFormatter; namespace IntelliTrader.Backtesting { [ZeroFormattable] public class TickerData : ITicker { [Index(0)] public virtual string Pair { get; set; } [Index(1)] public virtual decimal BidPrice { get; set; } [Index(2)] public virtual decimal AskPrice { get; set; } [Index(3)] public virtual decimal LastPrice { get; set; } public ITicker ToTicker() { return new Ticker { Pair = Pair, BidPrice = BidPrice, AskPrice = AskPrice, LastPrice = LastPrice }; } public static TickerData FromTicker(ITicker ticker) { return new TickerData { Pair = ticker.Pair, BidPrice = ticker.BidPrice, AskPrice = ticker.AskPrice, LastPrice = ticker.LastPrice }; } } } ================================================ FILE: IntelliTrader.Backtesting/Services/BacktestingExchangeService.cs ================================================ using ExchangeSharp; using IntelliTrader.Core; using IntelliTrader.Exchange.Base; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace IntelliTrader.Backtesting { public class BacktestingExchangeService : ExchangeService { private readonly IBacktestingService backtestingService; private ConcurrentBag markets; public BacktestingExchangeService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService, IBacktestingService backtestingService) : base(loggingService, healthCheckService, tasksService) { this.backtestingService = backtestingService; } public override void Start(bool virtualTrading) { loggingService.Info("Start Backtesting Exchange service..."); Api = InitializeApi(); loggingService.Info("Backtesting Exchange service started"); } public override void Stop() { loggingService.Info("Stop Backtesting Exchange service..."); loggingService.Info("Backtesting Exchange service stopped"); } protected override ExchangeAPI InitializeApi() { return new ExchangeBinanceAPI(); } public override IEnumerable GetMarkets() { if (markets == null && backtestingService.GetCurrentTickers() != null) { this.markets = new ConcurrentBag(backtestingService.GetCurrentTickers().Keys .Select(pair => GetPairMarket(pair)).Distinct().ToList()); } return markets.AsEnumerable() ?? new List(); } public override IEnumerable GetMarketPairs(string market) { return backtestingService.GetCurrentTickers().Keys.Where(t => t.EndsWith(market)); } public override decimal GetPrice(string pair, TradePriceType priceType) { if (backtestingService.GetCurrentTickers().TryGetValue(pair, out ITicker ticker)) { if (priceType == TradePriceType.Ask) { return ticker.AskPrice; } else if (priceType == TradePriceType.Bid) { return ticker.BidPrice; } else { return ticker.LastPrice; } } else { return 0; } } public override decimal GetPriceSpread(string pair) { if (backtestingService.GetCurrentTickers().TryGetValue(pair, out ITicker ticker)) { return Utils.CalculatePercentage(ticker.BidPrice, ticker.AskPrice); } else { return 0; } } public override Arbitrage GetArbitrage(string pair, string tradingMarket, List arbitrageMarkets = null, ArbitrageType? arbitrageType = null) { if (arbitrageMarkets == null || !arbitrageMarkets.Any()) { arbitrageMarkets = new List { ArbitrageMarket.ETH, ArbitrageMarket.BNB, ArbitrageMarket.USDT }; } Arbitrage arbitrage = new Arbitrage { Market = arbitrageMarkets.First(), Type = arbitrageType ?? ArbitrageType.Direct }; try { if (tradingMarket == Constants.Markets.BTC) { foreach (var market in arbitrageMarkets) { string marketPair = ChangeMarket(pair, market.ToString()); string arbitragePair = GetArbitrageMarketPair(market); if (marketPair != pair && backtestingService.GetCurrentTickers().TryGetValue(pair, out ITicker pairTicker) && backtestingService.GetCurrentTickers().TryGetValue(marketPair, out ITicker marketTicker) && backtestingService.GetCurrentTickers().TryGetValue(arbitragePair, out ITicker arbitrageTicker)) { decimal directArbitragePercentage = 0; decimal reverseArbitragePercentage = 0; if (market == ArbitrageMarket.ETH) { directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100; reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100; } else if (market == ArbitrageMarket.BNB) { directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100; reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100; } else if (market == ArbitrageMarket.USDT) { directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice / arbitrageTicker.AskPrice - 1) * 100; reverseArbitragePercentage = (arbitrageTicker.BidPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100; } if ((directArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Direct)) { arbitrage.IsAssigned = true; arbitrage.Market = market; arbitrage.Type = ArbitrageType.Direct; arbitrage.Percentage = directArbitragePercentage; } if ((reverseArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Reverse)) { arbitrage.IsAssigned = true; arbitrage.Market = market; arbitrage.Type = ArbitrageType.Reverse; arbitrage.Percentage = reverseArbitragePercentage; } } } } } catch { } return arbitrage; } public override string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket) { if (arbitrageMarket == ArbitrageMarket.ETH) { return Constants.Markets.ETH + Constants.Markets.BTC; } else if (arbitrageMarket == ArbitrageMarket.BNB) { return Constants.Markets.BNB + Constants.Markets.BTC; } else if (arbitrageMarket == ArbitrageMarket.USDT) { return Constants.Markets.BTC + Constants.Markets.USDT; } else { throw new NotSupportedException($"Unsupported arbitrage market: {arbitrageMarket}"); } } #region Not Needed For Backtesting public override IOrderDetails PlaceOrder(IOrder order) { throw new NotImplementedException(); } public override IEnumerable GetTickers() { throw new NotImplementedException(); } public override Dictionary GetAvailableAmounts() { throw new NotImplementedException(); } public override IEnumerable GetTrades(string pair) { throw new NotImplementedException(); } #endregion Not Needed For Backtesting } } ================================================ FILE: IntelliTrader.Backtesting/Services/BacktestingService.cs ================================================ using IntelliTrader.Core; using System; using System.Linq; using System.Collections.Generic; using System.IO; using System.Threading; using IntelliTrader.Signals.Base; using IntelliTrader.Trading; namespace IntelliTrader.Backtesting { internal class BacktestingService : ConfigrableServiceBase, IBacktestingService { public const string SNAPSHOT_FILE_EXTENSION = "bin"; public override string ServiceName => Constants.ServiceNames.BacktestingService; IBacktestingConfig IBacktestingService.Config => Config; public object SyncRoot { get; private set; } = new object(); private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITasksService tasksService; private ISignalsService signalsService; private ITradingService tradingService; private BacktestingLoadSnapshotsTimedTask backtestingLoadSnapshotsTimedTask; private BacktestingSaveSnapshotsTimedTask backtestingSaveSnapshotsTimedTask; public BacktestingService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tasksService = tasksService; } public void Start() { loggingService.Info($"Start Backtesting service... (Replay: {Config.Replay})"); signalsService = Application.Resolve(); tradingService = Application.Resolve(); if (Config.Replay) { backtestingLoadSnapshotsTimedTask = tasksService.AddTask( name: nameof(BacktestingLoadSnapshotsTimedTask), task: new BacktestingLoadSnapshotsTimedTask(loggingService, healthCheckService, tradingService, this), interval: Config.SnapshotsInterval / Config.ReplaySpeed * 1000, startDelay: Constants.TaskDelays.HighDelay, startTask: false, runNow: false, skipIteration: 0); } backtestingSaveSnapshotsTimedTask = tasksService.AddTask( name: nameof(BacktestingSaveSnapshotsTimedTask), task: new BacktestingSaveSnapshotsTimedTask(loggingService, healthCheckService, tradingService, signalsService, this), interval: Config.SnapshotsInterval * 1000, startDelay: Constants.TaskDelays.HighDelay, startTask: false, runNow: false, skipIteration: 0); if (Config.DeleteLogs) { loggingService.DeleteAllLogs(); } string virtualAccountPath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath); if (File.Exists(virtualAccountPath) && (Config.DeleteAccountData || !String.IsNullOrWhiteSpace(Config.CopyAccountDataPath))) { File.Delete(virtualAccountPath); } if (!String.IsNullOrWhiteSpace(Config.CopyAccountDataPath)) { File.Copy(Path.Combine(Directory.GetCurrentDirectory(), Config.CopyAccountDataPath), virtualAccountPath, true); } if (Config.Replay) { Application.Speed = Config.ReplaySpeed; } Application.Resolve().Started += OnCoreServiceStarted; loggingService.Info("Backtesting service started"); } public void Stop() { loggingService.Info("Stop Backtesting service..."); if (Config.Replay) { tasksService.RemoveTask(nameof(BacktestingLoadSnapshotsTimedTask), stopTask: true); } tasksService.RemoveTask(nameof(BacktestingSaveSnapshotsTimedTask), stopTask: true); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotTaken); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotTaken); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotLoaded); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotLoaded); Application.Resolve().Started -= OnCoreServiceStarted; loggingService.Info("Backtesting service stopped"); } public void Complete(int skippedSignalSnapshots, int skippedTickerSnapshots) { loggingService.Info("Backtesting results:"); double lagAmount = 0; foreach (var kvp in tasksService.GetAllTasks().OrderBy(t => t.Key)) { string taskName = kvp.Key; ITimedTask task = kvp.Value; double averageWaitTime = Math.Round(task.TotalLagTime / task.RunCount, 3); if (averageWaitTime > 0) lagAmount += averageWaitTime; loggingService.Info($" [+] {taskName} Run times: {task.RunCount}, average wait time: " + averageWaitTime); } loggingService.Info($"Lag value: {lagAmount}. Lower the ReplaySpeed if lag value is positive."); loggingService.Info($"Skipped signal snapshots: {skippedSignalSnapshots}"); loggingService.Info($"Skipped ticker snapshots: {skippedTickerSnapshots}"); tradingService.SuspendTrading(forced: true); signalsService.StopTrailing(); signalsService.Stop(); } public string GetSnapshotFilePath(string snapshotEntity) { var date = DateTimeOffset.UtcNow; return Path.Combine( Directory.GetCurrentDirectory(), Config.SnapshotsPath, snapshotEntity, date.ToString("yyyy-MM-dd"), date.ToString("HH"), date.ToString("mm-ss-fff") ) + "." + SNAPSHOT_FILE_EXTENSION; } public Dictionary> GetCurrentSignals() { return backtestingLoadSnapshotsTimedTask.GetCurrentSignals() ?? new Dictionary>(); } public Dictionary GetCurrentTickers() { return backtestingLoadSnapshotsTimedTask.GetCurrentTickers() ?? new Dictionary(); } public int GetTotalSnapshots() { return backtestingLoadSnapshotsTimedTask.GetTotalSnapshots(); } private void OnCoreServiceStarted() { tasksService.GetTask(nameof(TradingTimedTask)).SkipIteration = Config.TradingSpeedEasing; tasksService.GetTask(nameof(TradingTimedTask)).LoggingEnabled = false; tasksService.GetTask(nameof(TradingRulesTimedTask)).SkipIteration = Config.TradingRulesSpeedEasing; tasksService.GetTask(nameof(SignalRulesTimedTask)).SkipIteration = Config.SignalRulesSpeedEasing; tasksService.GetTask(nameof(SignalRulesTimedTask)).LoggingEnabled = false; } } } ================================================ FILE: IntelliTrader.Backtesting/Services/BacktestingSignalsService.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Signals.Base; using System; using System.Collections.Generic; using System.Linq; namespace IntelliTrader.Backtesting { public class BacktestingSignalsService : ConfigrableServiceBase, ISignalsService { public override string ServiceName => Constants.ServiceNames.SignalsService; ISignalsConfig ISignalsService.Config => Config; public IModuleRules Rules { get; private set; } public ISignalRulesConfig RulesConfig { get; private set; } private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITasksService tasksService; private readonly ITradingService tradingService; private readonly IRulesService rulesService; private readonly IBacktestingService backtestingService; private SignalRulesTimedTask signalRulesTimedTask; private IEnumerable signalNames; public BacktestingSignalsService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService, ITradingService tradingService, IRulesService rulesService, IBacktestingService backtestingService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tasksService = tasksService; this.tradingService = tradingService; this.rulesService = rulesService; this.backtestingService = backtestingService; } public void Start() { loggingService.Info("Start Backtesting Signals service..."); OnSignalRulesChanged(); rulesService.RegisterRulesChangeCallback(OnSignalRulesChanged); signalRulesTimedTask = tasksService.AddTask( name: nameof(SignalRulesTimedTask), task: new SignalRulesTimedTask(loggingService, healthCheckService, tradingService, rulesService, this), interval: RulesConfig.CheckInterval * 1000 / Application.Speed, startDelay: Constants.TaskDelays.LowDelay, startTask: false, runNow: false, skipIteration: 0); loggingService.Info("Backtesting Signals service started"); } public void Stop() { loggingService.Info("Stop Backtesting Signals service..."); tasksService.RemoveTask(nameof(SignalRulesTimedTask), stopTask: true); rulesService.UnregisterRulesChangeCallback(OnSignalRulesChanged); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.SignalRulesProcessed); loggingService.Info("Backtesting Signals service stopped"); } public void ProcessPair(string pair, Dictionary signals) { IEnumerable enabledRules = Rules.Entries.Where(r => r.Enabled); foreach (IRule rule in enabledRules) { signalRulesTimedTask.ProcessRule(rule, signals, pair, signalRulesTimedTask.GetExcludedPairs(), GetGlobalRating()); } } public void StopTrailing() { signalRulesTimedTask.StopTrailing(); } public List GetTrailingSignals() { return signalRulesTimedTask.GetTrailingSignals(); } public IEnumerable GetTrailingInfo(string pair) { return signalRulesTimedTask.GetTrailingInfo(pair); } public IEnumerable GetSignalNames() { if (signalNames == null) { signalNames = backtestingService.GetCurrentSignals().Values.SelectMany(val => val.Select(s => s.Name)).Distinct().ToList(); } return signalNames; } public IEnumerable GetAllSignals() { return GetSignalsByName(null); } public IEnumerable GetSignalsByName(string signalName) { IEnumerable allSignals = backtestingService.GetCurrentSignals().SelectMany(s => s.Value); if (signalName == null) { return allSignals; } else { return allSignals.Where(s => s.Name == signalName); } } public IEnumerable GetSignalsByPair(string pair) { if (backtestingService.GetCurrentSignals().TryGetValue(pair, out IEnumerable signalsByPair)) { return signalsByPair; } else { return null; } } public ISignal GetSignal(string pair, string signalName) { return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair); } public double? GetRating(string pair, string signalName) { return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating; } public double? GetRating(string pair, IEnumerable signalNames) { if (signalNames != null && signalNames.Count() > 0) { double ratingSum = 0; foreach (var signalName in signalNames) { var rating = GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating; if (rating != null) { ratingSum += rating.Value; } else { return null; } } return Math.Round(ratingSum / signalNames.Count(), 8); } else { return null; } } public double? GetGlobalRating() { try { double ratingSum = 0; double ratingCount = 0; var currentSignals = backtestingService.GetCurrentSignals(); if (currentSignals != null) { var signalGroups = currentSignals.Values.SelectMany(s => s).GroupBy(s => s.Name); foreach (var signalGroup in signalGroups) { if (Config.GlobalRatingSignals.Contains(signalGroup.Key)) { double? averageRating = signalGroup.Average(s => s.Rating); if (averageRating != null) { ratingSum += averageRating.Value; ratingCount++; } } } } if (ratingCount > 0) { return Math.Round(ratingSum / ratingCount, 8); } else { return null; } } catch (Exception ex) { loggingService.Error("Unable to get global rating", ex); return null; } } private void OnSignalRulesChanged() { Rules = rulesService.GetRules(ServiceName); RulesConfig = Rules.GetConfiguration(); } } } ================================================ FILE: IntelliTrader.Backtesting/TimedTasks/BacktestingLoadSnapshotsTimedTask.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using ZeroFormatter; namespace IntelliTrader.Backtesting { internal class BacktestingLoadSnapshotsTimedTask : HighResolutionTimedTask { private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITradingService tradingService; private readonly IBacktestingService backtestingService; private Queue allSignalSnapshotPaths; private Queue allTickerSnapshotPaths; private Dictionary> currentSignals; private Dictionary currentTickers; private Stopwatch backtestingTimer; private int totalSignalSnapshots; private int totalTickerSnapshots; public int loadedSignalSnapshots; public int loadedTickerSnapshots; private bool isCompleted; public object Config { get; } public BacktestingLoadSnapshotsTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService, IBacktestingService backtestingService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tradingService = tradingService; this.backtestingService = backtestingService; PopulateSnapshotPaths(); } protected override void Run() { if (!isCompleted) { LoadNextSnapshots(); } } public Dictionary> GetCurrentSignals() { lock (backtestingService.SyncRoot) { return currentSignals; } } public Dictionary GetCurrentTickers() { lock (backtestingService.SyncRoot) { return currentTickers; } } public int GetTotalSnapshots() { return totalSignalSnapshots; } private void LoadNextSnapshots() { lock (backtestingService.SyncRoot) { if (loadedSignalSnapshots == 0 && loadedTickerSnapshots == 0) { loggingService.Info($"<<<--- Backtesting started. Total signals snapshots: {totalSignalSnapshots}, Total tickers snapshots: {totalTickerSnapshots} --->>>"); backtestingTimer = Stopwatch.StartNew(); } if (allSignalSnapshotPaths.TryDequeue(out string currentSignalsSnapshotPath)) { try { byte[] currentSignalsSnapshotBytes = File.ReadAllBytes(currentSignalsSnapshotPath); IEnumerable data = ZeroFormatterSerializer.Deserialize>(currentSignalsSnapshotBytes).Select(s => s.ToSignal()).ToList(); currentSignals = data.GroupBy(s => s.Pair).ToDictionary(s => s.Key, s => s.AsEnumerable()); loadedSignalSnapshots++; var currentSignalsSnapshotFile = currentSignalsSnapshotPath.Substring(currentSignalsSnapshotPath.Length - 27); currentSignalsSnapshotFile = currentSignalsSnapshotFile.Replace('\\', '-').Replace('/', '-'); if (backtestingService.Config.ReplayOutput && loadedSignalSnapshots % 100 == 0) { loggingService.Info($"<<<--- ({loadedSignalSnapshots}/{totalSignalSnapshots}) Load signals snapshot file: {currentSignalsSnapshotFile} --->>>"); } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotLoaded, $"File: {currentSignalsSnapshotFile}"); } catch (Exception ex) { loggingService.Error($"<<<--- Unable to load signals snapshot file: {currentSignalsSnapshotPath} --->>>", ex); } } if (allTickerSnapshotPaths.TryDequeue(out string currentTickersSnapshotPath)) { try { byte[] currentTickersSnapshotBytes = File.ReadAllBytes(currentTickersSnapshotPath); IEnumerable data = ZeroFormatterSerializer.Deserialize>(currentTickersSnapshotBytes).Select(t => t.ToTicker()).ToList(); currentTickers = data.ToDictionary(t => t.Pair, t => t); loadedTickerSnapshots++; var currentTickersSnapshotFile = currentTickersSnapshotPath.Substring(currentTickersSnapshotPath.Length - 27); currentTickersSnapshotFile = currentTickersSnapshotFile.Replace('\\', '-').Replace('/', '-'); if (backtestingService.Config.ReplayOutput && loadedTickerSnapshots % 100 == 0) { loggingService.Info($"<<<--- ({loadedTickerSnapshots}/{totalTickerSnapshots}) Load tickers snapshot file: {currentTickersSnapshotFile} --->>>"); } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotLoaded, $"File: {currentTickersSnapshotFile}"); } catch (Exception ex) { loggingService.Error($"<<<--- Unable to load tickers snapshot file: {currentTickersSnapshotPath} --->>>", ex); } } if (currentSignalsSnapshotPath == null && currentTickersSnapshotPath == null) { isCompleted = true; backtestingTimer.Stop(); loggingService.Info($"<<<--- Backtesting finished in {Math.Round(backtestingTimer.Elapsed.TotalSeconds)} seconds --->>>"); backtestingService.Complete(totalSignalSnapshots - loadedSignalSnapshots, totalTickerSnapshots - loadedTickerSnapshots); } } } private void PopulateSnapshotPaths() { var signalsSnapshotPath = Path.Combine(Directory.GetCurrentDirectory(), backtestingService.Config.SnapshotsPath, Constants.SnapshotEntities.Signals); if (Directory.Exists(signalsSnapshotPath)) { var files = Directory.EnumerateFiles(signalsSnapshotPath, "*." + BacktestingService.SNAPSHOT_FILE_EXTENSION, SearchOption.AllDirectories); allSignalSnapshotPaths = new Queue(files.Take(backtestingService.Config.ReplayEndIndex ?? files.Count()).Skip(backtestingService.Config.ReplayStartIndex ?? 0)); } else { allSignalSnapshotPaths = new Queue(); } totalSignalSnapshots = allSignalSnapshotPaths.Count; var tickersSnapshotPath = Path.Combine(Directory.GetCurrentDirectory(), backtestingService.Config.SnapshotsPath, Constants.SnapshotEntities.Tickers); if (Directory.Exists(tickersSnapshotPath)) { var files = Directory.EnumerateFiles(tickersSnapshotPath, "*." + BacktestingService.SNAPSHOT_FILE_EXTENSION, SearchOption.AllDirectories); allTickerSnapshotPaths = new Queue(files.Take(backtestingService.Config.ReplayEndIndex ?? files.Count()).Skip(backtestingService.Config.ReplayStartIndex ?? 0)); } else { allTickerSnapshotPaths = new Queue(); } totalTickerSnapshots = allTickerSnapshotPaths.Count; } } } ================================================ FILE: IntelliTrader.Backtesting/TimedTasks/BacktestingSaveSnapshotsTimedTask.cs ================================================ using IntelliTrader.Core; using System.IO; using System.Linq; using ZeroFormatter; namespace IntelliTrader.Backtesting { internal class BacktestingSaveSnapshotsTimedTask : HighResolutionTimedTask { private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITradingService tradingService; private readonly ISignalsService signalsService; private readonly IBacktestingService backtestingService; public BacktestingSaveSnapshotsTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService, ISignalsService signalsService, IBacktestingService backtestingService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tradingService = tradingService; this.signalsService = signalsService; this.backtestingService = backtestingService; } protected override void Run() { if (backtestingService.Config.Enabled && !backtestingService.Config.Replay) { TakeSignalsSnapshot(); TakeTickersSnapshot(); } } private void TakeSignalsSnapshot() { var signals = signalsService.GetAllSignals().Select(s => SignalData.FromSignal(s)); byte[] signalBytes = ZeroFormatterSerializer.Serialize(signals); string signalsSnapshotFilePath = backtestingService.GetSnapshotFilePath(Constants.SnapshotEntities.Signals); var signalsSnapshotFile = new FileInfo(signalsSnapshotFilePath); signalsSnapshotFile.Directory.Create(); File.WriteAllBytes(signalsSnapshotFilePath, signalBytes); healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingSignalsSnapshotTaken, $"Signals: {signals.Count()}"); } private void TakeTickersSnapshot() { var tickers = tradingService.Exchange.GetTickers().Select(t => TickerData.FromTicker(t)); byte[] tickerBytes = ZeroFormatterSerializer.Serialize(tickers); string tickersSnapshotFilePath = backtestingService.GetSnapshotFilePath(Constants.SnapshotEntities.Tickers); var tickersSnapshotFile = new FileInfo(tickersSnapshotFilePath); tickersSnapshotFile.Directory.Create(); File.WriteAllBytes(tickersSnapshotFilePath, tickerBytes); healthCheckService.UpdateHealthCheck(Constants.HealthChecks.BacktestingTickersSnapshotTaken, $"Tickers: {tickers.Count()}"); } } } ================================================ FILE: IntelliTrader.Core/AppModule.cs ================================================ using Autofac; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().As().Named(Constants.ServiceNames.CoreService).SingleInstance(); builder.RegisterType().As().As().Named(Constants.ServiceNames.LoggingService).SingleInstance(); builder.RegisterType().As().As().Named(Constants.ServiceNames.NotificationService).SingleInstance(); } } } ================================================ FILE: IntelliTrader.Core/Application.cs ================================================ using Autofac; using Autofac.Core; using System; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; namespace IntelliTrader.Core { public class Application { public readonly static IConfigProvider ConfigProvider = new ConfigProvider(); public static double Speed { get; set; } = 1; public static ILifetimeScope Container { get { RegisterComponents(); return container; } } private static IContainer container; public static void RegisterComponents(bool repos = true, bool queries = true, bool mappers = true) { if (Application.container == null) { var builder = new ContainerBuilder(); var assemblyPattern = new Regex($"{nameof(IntelliTrader)}.*.dll"); var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => assemblyPattern.IsMatch(Path.GetFileName(a.Location))); var dynamicAssembliesPath = new Uri(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location)).LocalPath; var dynamicAssemblies = Directory.EnumerateFiles(dynamicAssembliesPath, "*.dll", SearchOption.AllDirectories) .Where(filename => assemblyPattern.IsMatch(Path.GetFileName(filename)) && !loadedAssemblies.Any(a => Path.GetFileName(a.Location) == Path.GetFileName(filename))); var allAssemblies = loadedAssemblies.Concat(dynamicAssemblies.Select(Assembly.LoadFrom)).Distinct(); builder.RegisterAssemblyModules(allAssemblies.ToArray()); Application.container = builder.Build(); } } public static TService Resolve(params Parameter[] parameters) where TService : class { return Container.Resolve(parameters); } public static TService ResolveNamed(string name, params Parameter[] parameters) where TService : class { return Container.ResolveNamed(name, parameters); } public static TService ResolveOptional(params Parameter[] parameters) where TService : class { return Container.ResolveOptional(parameters); } public static TService ResolveOptionalNamed(string name, params Parameter[] parameters) where TService : class { return Container.ResolveOptionalNamed(name, parameters); } } } ================================================ FILE: IntelliTrader.Core/IntelliTrader.Core.csproj ================================================ netcoreapp2.1 1.1.0.0 1.1.0.0 ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/IBacktestingConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IBacktestingConfig { bool Enabled { get; } bool Replay { get; } bool ReplayOutput { get; } double ReplaySpeed { get; } int? ReplayStartIndex { get; } int? ReplayEndIndex { get; } bool DeleteLogs { get; } bool DeleteAccountData { get; } string CopyAccountDataPath { get; } int TradingSpeedEasing { get; } int TradingRulesSpeedEasing { get; } int SignalRulesSpeedEasing { get; } int SnapshotsInterval { get; } string SnapshotsPath { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/IConfigProvider.cs ================================================ using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IConfigProvider { string GetSectionJson(string sectionName); void SetSectionJson(string sectionName, string definition); IConfigurationSection GetSection(string sectionName, Action onChange = null); T GetSection(string sectionName, Action onChange = null); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/ICoreConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ICoreConfig { bool DebugMode { get; } bool PasswordProtected { get; } string Password { get; } string InstanceName { get; } double TimezoneOffset { get; } bool HealthCheckEnabled { get; set; } double HealthCheckInterval { get; } double HealthCheckSuspendTradingTimeout { get; } int HealthCheckFailuresToRestartServices { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/ILoggingConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ILoggingConfig { bool Enabled { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/INotificationConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface INotificationConfig { bool Enabled { get; } bool TelegramEnabled { get; } string TelegramBotToken { get; } long TelegramChatId { get; } bool TelegramAlertsEnabled { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/IRulesConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IRulesConfig { IEnumerable Modules { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/ISignalsConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISignalsConfig { bool Enabled { get; } IEnumerable GlobalRatingSignals { get; } IEnumerable Definitions { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/ITradingConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ITradingConfig : IBuyConfig, IBuyDCAConfig, ISellConfig, ISellDCAConfig { bool Enabled { get; } string Market { get; } string Exchange { get; } int MaxPairs { get; } decimal MinCost { get; } List ExcludedPairs { get; } TradePriceType TradePriceType { get; set; } bool RepeatLastDCALevel { get; } List DCALevels { get; } double TradingCheckInterval { get; } double AccountRefreshInterval { get; } decimal AccountInitialBalance { get; } DateTimeOffset AccountInitialBalanceDate { get; } string AccountFilePath { get; } bool VirtualTrading { get; } decimal VirtualTradingFees { get; } decimal VirtualAccountInitialBalance { get; } string VirtualAccountFilePath { get; } ITradingConfig Clone(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Configs/IWebConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IWebConfig { bool Enabled { get; } bool DebugMode { get; } bool ReadOnlyMode { get; } int Port { get; } bool SSLEnabled { get; } string SSLCertPath { get; set; } string SSLCertPassword { get; set; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Exchange/ITicker.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ITicker { string Pair { get; } decimal BidPrice { get; } decimal AskPrice { get; } decimal LastPrice { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/IHealthCheck.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IHealthCheck { string Name { get; } string Message { get; } DateTimeOffset LastUpdated { get; } bool Failed { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Rules/IModuleRules.cs ================================================ using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IModuleRules { string Module { get; } IConfigurationSection Configuration { get; } IEnumerable Entries { get; } T GetConfiguration(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Rules/IRule.cs ================================================ using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IRule { bool Enabled { get; } string Name { get; } RuleAction Action { get; } IEnumerable Conditions { get; } IRuleTrailing Trailing { get; } IConfigurationSection Modifiers { get; } T GetModifiers(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Rules/IRuleCondition.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IRuleCondition { string Signal { get; } decimal? MinPrice { get; } decimal? MaxPrice { get; } decimal? MinSpread { get; } decimal? MaxSpread { get; } long? MinVolume { get; } long? MaxVolume { get; } double? MinVolumeChange { get; } double? MaxVolumeChange { get; } decimal? MinPriceChange { get; } decimal? MaxPriceChange { get; } double? MinRating { get; } double? MaxRating { get; } double? MinRatingChange { get; } double? MaxRatingChange { get; } double? MinVolatility { get; } double? MaxVolatility { get; } double? MinGlobalRating { get; } double? MaxGlobalRating { get; } decimal? MinArbitrage { get; } decimal? MaxArbitrage { get; } ArbitrageMarket? ArbitrageMarket { get; } ArbitrageType? ArbitrageType { get; } List Pairs { get; } List NotPairs { get; } double? MinAge { get; } double? MaxAge { get; } double? MinLastBuyAge { get; } double? MaxLastBuyAge { get; } decimal? MinMargin { get; } decimal? MaxMargin { get; } decimal? MinMarginChange { get; } decimal? MaxMarginChange { get; } decimal? MinAmount { get; set; } decimal? MaxAmount { get; set; } decimal? MinCost { get; } decimal? MaxCost { get; } int? MinDCALevel { get; } int? MaxDCALevel { get; } List SignalRules { get; } List NotSignalRules { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Rules/IRuleTrailing.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IRuleTrailing { bool Enabled { get; } int MinDuration { get; } int MaxDuration { get; } IEnumerable StartConditions { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Rules/ISignalRulesConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISignalRulesConfig { RuleProcessingMode ProcessingMode { get; } double CheckInterval { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Rules/RuleProcessingMode.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum RuleProcessingMode { FirstMatch, AllMatches } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/Base/IConfigurableService.cs ================================================ using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IConfigurableService : INamedService { IConfigurationSection RawConfig { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/Base/INamedService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface INamedService { string ServiceName { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/IBacktestingService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IBacktestingService : IConfigurableService { IBacktestingConfig Config { get; } object SyncRoot { get; } void Start(); void Stop(); void Complete(int skippedSignalSnapshots, int skippedTickerSnapshots); string GetSnapshotFilePath(string snapshotEntity); Dictionary> GetCurrentSignals(); Dictionary GetCurrentTickers(); int GetTotalSnapshots(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/ICoreService.cs ================================================ using System; using System.Collections.Concurrent; namespace IntelliTrader.Core { public interface ICoreService : IConfigurableService { event Action Started; ICoreConfig Config { get; } string Version { get; } void Start(); void Stop(); void Restart(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/IExchangeService.cs ================================================ using System; using System.Collections.Generic; namespace IntelliTrader.Core { public interface IExchangeService : IConfigurableService { void Start(bool virtualTrading); void Stop(); IOrderDetails PlaceOrder(IOrder order); decimal ClampOrderAmount(string pair, decimal amount); decimal ClampOrderPrice(string pair, decimal price); void ConnectTickersWebsocket(); void DisconnectTickersWebsocket(); IEnumerable GetTickers(); IEnumerable GetMarkets(); IEnumerable GetMarketPairs(string market); Dictionary GetAvailableAmounts(); IEnumerable GetTrades(string pair); decimal GetPrice(string pair, TradePriceType priceType); decimal GetPriceSpread(string pair); Arbitrage GetArbitrage(string pair, string tradingMarket, List arbitrageMarkets = null, ArbitrageType? arbitrageType = null); string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket); string GetPairMarket(string pair); string ChangeMarket(string pair, string market); decimal ConvertPrice(string pair, decimal price, string market, TradePriceType priceType); TimeSpan GetTimeElapsedSinceLastTickersUpdate(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/IHealthCheckService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IHealthCheckService { void Start(); void Stop(); void UpdateHealthCheck(string name, string message = null, bool failed = false); void RemoveHealthCheck(string name); IEnumerable GetHealthChecks(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/ILoggingService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ILoggingService : IConfigurableService { void Debug(string message, Exception exception = null); void Debug(string message, params object[] propertyValues); void Error(string message, Exception exception = null); void Error(string message, params object[] propertyValues); void Fatal(string message, Exception exception = null); void Fatal(string message, params object[] propertyValues); void Info(string message, Exception exception = null); void Info(string message, params object[] propertyValues); void Verbose(string message, Exception exception = null); void Verbose(string message, params object[] propertyValues); void Warning(string message, Exception exception = null); void Warning(string message, params object[] propertyValues); void DeleteAllLogs(); string[] GetLogEntries(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/INotificationService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface INotificationService { INotificationConfig Config { get; } void Start(); void Stop(); void Notify(string message); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/IOrderingService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IOrderingService { IOrderDetails PlaceBuyOrder(BuyOptions options); IOrderDetails PlaceSellOrder(SellOptions options); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/IRulesService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IRulesService : IConfigurableService { IRulesConfig Config { get; } IModuleRules GetRules(string module); bool CheckConditions(IEnumerable conditions, Dictionary signals, double? globalRating, string pair, ITradingPair tradingPair); void RegisterRulesChangeCallback(Action callback); void UnregisterRulesChangeCallback(Action callback); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/ISignalsService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISignalsService : IConfigurableService { ISignalsConfig Config { get; } IModuleRules Rules { get; } ISignalRulesConfig RulesConfig { get; } void Start(); void Stop(); void ProcessPair(string pair, Dictionary signals); void StopTrailing(); List GetTrailingSignals(); IEnumerable GetTrailingInfo(string pair); IEnumerable GetSignalNames(); IEnumerable GetAllSignals(); IEnumerable GetSignalsByName(string signalName); IEnumerable GetSignalsByPair(string pair); double? GetRating(string pair, string signalName); double? GetRating(string pair, IEnumerable signalNames); double? GetGlobalRating(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/ITasksService.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ITasksService { T AddTask(string name, T task, double interval, double startDelay = 0, bool startTask = true, bool runNow = false, int skipIteration = 0) where T : ITimedTask; void RemoveTask(string name, bool stopTask = true); void StartAllTasks(); void StopAllTasks(); void RemoveAllTasks(); ITimedTask GetTask(string name); T GetTask(string name); IEnumerable> GetAllTasks(); void SetUnhandledExceptionHandler(UnhandledExceptionEventHandler handler); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/ITradingService.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace IntelliTrader.Core { public interface ITradingService : IConfigurableService { ITradingConfig Config { get; } IModuleRules Rules { get; } IExchangeService Exchange { get; } ITradingAccount Account { get; } ConcurrentStack OrderHistory { get; } bool IsTradingSuspended { get; } void Start(); void Stop(); void ResumeTrading(bool forced = false); void SuspendTrading(bool forced = false); IPairConfig GetPairConfig(string pair); void ReapplyTradingRules(); void Buy(BuyOptions options); void Sell(SellOptions options); void Swap(SwapOptions options); void Arbitrage(ArbitrageOptions options); bool CanBuy(BuyOptions options, out string message); bool CanSell(SellOptions options, out string message); bool CanSwap(SwapOptions options, out string message); bool CanArbitrage(ArbitrageOptions options, out string message); decimal GetPrice(string pair, TradePriceType? priceType = null, bool normalize = true); decimal CalculateOrderFees(IOrderDetails order); bool IsNormalizedPair(string pair); string NormalizePair(string pair); void LogOrder(IOrderDetails order); List GetTrailingBuys(); List GetTrailingSells(); void StopTrailingBuy(string pair); void StopTrailingSell(string pair); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Services/IWebService.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IWebService { IWebConfig Config { get; } void Start(); void Stop(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Signals/ISignal.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISignal { string Name { get; } string Pair { get; } long? Volume { get; } double? VolumeChange { get; set; } decimal? Price { get; } decimal? PriceChange { get; } double? Rating { get; } double? RatingChange { get; } double? Volatility { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Signals/ISignalDefinition.cs ================================================ using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISignalDefinition { string Name { get; } string Receiver { get; } IConfigurationSection Configuration { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Signals/ISignalTrailingInfo.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISignalTrailingInfo { IRule Rule { get; } DateTimeOffset StartTime { get; } double Duration { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Tasks/ITimedTask.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; namespace IntelliTrader.Core { public interface ITimedTask { event UnhandledExceptionEventHandler UnhandledException; double StartDelay { get; set; } double Interval { get; set; } int SkipIteration { get; set; } Stopwatch Stopwatch { get; set; } bool IsRunning { get; } long RunCount { get; } double TotalRunTime { get; } double TotalLagTime { get; } void Start(); void Stop(); void Pause(); void Continue(); void RunNow(); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/IBuyConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IBuyConfig { bool BuyEnabled { get; set; } OrderType BuyType { get; } decimal BuyMaxCost { get; } decimal BuyMultiplier { get; } decimal BuyMinBalance { get; } double BuySamePairTimeout { get; } decimal BuyTrailing { get; } decimal BuyTrailingStopMargin { get; } BuyTrailingStopAction BuyTrailingStopAction { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/IBuyDCAConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IBuyDCAConfig { bool BuyDCAEnabled { get; set; } decimal BuyDCAMultiplier { get; } decimal BuyDCAMinBalance { get; } double BuyDCASamePairTimeout { get; } decimal BuyDCATrailing { get; } decimal BuyDCATrailingStopMargin { get; } BuyTrailingStopAction BuyDCATrailingStopAction { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/IOrder.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IOrder { OrderSide Side { get; } OrderType Type { get; } DateTimeOffset Date { get; } string Pair { get; } decimal Amount { get; } decimal Price { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/IOrderDetails.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IOrderDetails { bool IsNormalized { get; } OrderSide Side { get; } OrderResult Result { get; } DateTimeOffset Date { get; } string OrderId { get; } string Pair { get; } string OriginalPair { get; } string Message { get; } decimal Amount { get; } decimal AmountFilled { get; } decimal Price { get; } decimal AveragePrice { get; } decimal Fees { get; } string FeesCurrency { get; } decimal Cost { get; } OrderMetadata Metadata { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/IPairConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface IPairConfig : IBuyConfig, ISellConfig { IEnumerable Rules { get; } int MaxPairs { get; } bool SwapEnabled { get; } List SwapSignalRules { get; } int SwapTimeout { get; } bool ArbitrageEnabled { get; } List ArbitrageMarkets { get; } ArbitrageType? ArbitrageType { get; } decimal? ArbitrageBuyMultiplier { get; } decimal? ArbitrageSellMultiplier { get; } List ArbitrageSignalRules { get; } decimal? CurrentDCAMargin { get; } decimal? NextDCAMargin { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/ISellConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISellConfig { bool SellEnabled { get; set; } OrderType SellType { get; } decimal SellMargin { get; } decimal SellTrailing { get; } decimal SellTrailingStopMargin { get; } SellTrailingStopAction SellTrailingStopAction { get; } bool SellStopLossEnabled { get; } bool SellStopLossAfterDCA { get; } double SellStopLossMinAge { get; } decimal SellStopLossMargin { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/ISellDCAConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ISellDCAConfig { decimal SellDCAMargin { get; } decimal SellDCATrailing { get; } decimal SellDCATrailingStopMargin { get; } SellTrailingStopAction SellDCATrailingStopAction { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/ITradeResult.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ITradeResult { bool IsSuccessful { get; } bool IsSwap { get; } bool IsArbitrage { get; } string Pair { get; } decimal Amount { get; } List OrderDates { get; } decimal AveragePrice { get; } decimal Fees { get; } decimal FeesTotal { get; } decimal Cost { get; } DateTimeOffset SellDate { get; } decimal SellPrice { get; } decimal SellCost { get; } decimal BalanceOffset { get; } decimal Profit { get; } OrderMetadata Metadata { get; } } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/ITradingAccount.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ITradingAccount : IDisposable { object SyncRoot { get; } void Refresh(); void Save(); void AddOrder(IOrderDetails order); void AddBuyOrder(IOrderDetails order); ITradeResult AddSellOrder(IOrderDetails order); ITradingPair AddOrUpdatePair(IOrderDetails order, string pair, decimal feesMarketCurrency, decimal feesPairCurrency, decimal? amountOverride = null, decimal? averagePriceOverride = null); IOrderDetails AddBlankOrder(string pair, decimal amount, bool includeFees = true); void AddBalance(decimal balanceOffset); decimal GetBalance(); decimal GetTotalBalance(); bool HasTradingPair(string pair, bool includeDust = false); ITradingPair GetTradingPair(string pair, bool includeDust = false); IEnumerable GetTradingPairs(bool includeDust = false); } } ================================================ FILE: IntelliTrader.Core/Interfaces/Trading/ITradingPair.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public interface ITradingPair { string Pair { get; } string FormattedName { get; } int DCALevel { get; } List OrderIds { get; } List OrderDates { get; } decimal Amount { get; } decimal AveragePrice { get; } decimal Fees { get; } decimal Cost { get; } decimal? CostOverride { get; set; } decimal CurrentCost { get; } decimal CurrentPrice { get; } decimal CurrentSpread { get; } decimal CurrentMargin { get; } double CurrentAge { get; } double LastBuyAge { get; } OrderMetadata Metadata { get; } decimal GetPartialCost(decimal partialAmount); void OverrideCost(decimal? costOverride); void SetCurrentValues(decimal currentPrice, decimal currentSpread); void SetMetadata(OrderMetadata metadata); } } ================================================ FILE: IntelliTrader.Core/Models/Config/ConfigProvider.cs ================================================ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives; using System; using System.IO; namespace IntelliTrader.Core { internal class ConfigProvider : IConfigProvider { private const string ROOT_CONFIG_DIR = "config"; private const string PATHS_CONFIG_PATH = "paths.json"; private const string PATHS_SECTION_NAME = "Paths"; private IConfigurationSection paths; public ConfigProvider() { IConfigurationRoot pathsConfig = GetConfig(PATHS_CONFIG_PATH, changedPathsConfig => { paths = changedPathsConfig.GetSection(PATHS_SECTION_NAME); }); paths = pathsConfig.GetSection(PATHS_SECTION_NAME); } public string GetSectionJson(string sectionName) { try { string configPath = paths.GetValue(sectionName); var fullConfigPath = Path.Combine(Directory.GetCurrentDirectory(), ROOT_CONFIG_DIR, configPath); return File.ReadAllText(fullConfigPath); } catch (Exception ex) { Application.Resolve().Error($"Unable to load config section {sectionName}", ex); return null; } } public void SetSectionJson(string sectionName, string definition) { try { string configPath = paths.GetValue(sectionName); var fullConfigPath = Path.Combine(Directory.GetCurrentDirectory(), ROOT_CONFIG_DIR, configPath); File.WriteAllText(fullConfigPath, definition); } catch (Exception ex) { Application.Resolve().Error($"Unable to save config section {sectionName}", ex); } } public T GetSection(string sectionName, Action onChange = null) { IConfigurationSection configSection = GetSection(sectionName, changedConfigSection => { onChange?.Invoke(changedConfigSection.Get()); }); return configSection.Get(); } public IConfigurationSection GetSection(string sectionName, Action onChange = null) { string configPath = paths.GetValue(sectionName); IConfigurationRoot configRoot = GetConfig(configPath, changedConfigRoot => { onChange?.Invoke(changedConfigRoot.GetSection(sectionName)); }); return configRoot.GetSection(sectionName); } private IConfigurationRoot GetConfig(string configPath, Action onChange) { var fullConfigPath = Path.Combine(Directory.GetCurrentDirectory(), ROOT_CONFIG_DIR); var configBuilder = new ConfigurationBuilder() .SetBasePath(fullConfigPath) .AddJsonFile(configPath, optional: false, reloadOnChange: true) .AddEnvironmentVariables(); var configRoot = configBuilder.Build(); ChangeToken.OnChange(configRoot.GetReloadToken, () => onChange(configRoot)); return configRoot; } } } ================================================ FILE: IntelliTrader.Core/Models/Config/CoreConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { internal class CoreConfig : ICoreConfig { public bool DebugMode { get; set; } public bool PasswordProtected { get; set; } public string Password { get; set; } public string InstanceName { get; set; } public double TimezoneOffset { get; set; } public bool HealthCheckEnabled { get; set; } = true; public double HealthCheckInterval { get; set; } public double HealthCheckSuspendTradingTimeout { get; set; } public int HealthCheckFailuresToRestartServices { get; set; } } } ================================================ FILE: IntelliTrader.Core/Models/Config/LoggingConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { internal class LoggingConfig : ILoggingConfig { public bool Enabled { get; set; } } } ================================================ FILE: IntelliTrader.Core/Models/Config/NotificationConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { internal class NotificationConfig : INotificationConfig { public bool Enabled { get; set; } public bool TelegramEnabled { get; set; } public string TelegramBotToken { get; set; } public long TelegramChatId { get; set; } public bool TelegramAlertsEnabled { get; set; } } } ================================================ FILE: IntelliTrader.Core/Models/Constants.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public static class Constants { public static class ServiceNames { public const string CoreService = "Core"; public const string CachingService = "Caching"; public const string LoggingService = "Logging"; public const string TradingService = "Trading"; public const string ExchangeService = "Exchange"; public const string SignalsService = "Signals"; public const string RulesService = "Rules"; public const string NotificationService = "Notification"; public const string WebService = "Web"; public const string BacktestingService = "Backtesting"; public const string BacktestingExchangeService = "BacktestingExchange"; public const string BacktestingSignalsService = "BacktestingSignals"; } public static class HealthChecks { public const string AccountRefreshed = "Account refreshed"; public const string TickersUpdated = "Tickers updated"; public const string TradingPairsProcessed = "Trading pairs processed"; public const string TradingViewCryptoSignalsReceived = "TV Signals received"; public const string SignalRulesProcessed = "Signals rules processed"; public const string TradingRulesProcessed = "Trading rules processed"; public const string BacktestingSignalsSnapshotTaken = "Backtesting signals snapshot taken"; public const string BacktestingTickersSnapshotTaken = "Backtesting tickers snapshot taken"; public const string BacktestingSignalsSnapshotLoaded = "Backtesting signals snapshot loaded"; public const string BacktestingTickersSnapshotLoaded = "Backtesting tickers snapshot loaded"; } public static class SnapshotEntities { public const string Signals = "signals"; public const string Tickers = "tickers"; } public static class TaskDelays { public const int ZeroDelay = 0; public const int LowDelay = 1200; public const int MidDelay = 2400; public const int NormalDelay = 3300; public const int HighDelay = 4700; } public static class Markets { public const string BTC = "BTC"; public const string ETH = "ETH"; public const string BNB = "BNB"; public const string USDT = "USDT"; } } } ================================================ FILE: IntelliTrader.Core/Models/HealthCheck.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { internal class HealthCheck : IHealthCheck { public string Name { get; set; } public string Message { get; set; } public DateTimeOffset LastUpdated { get; set; } public bool Failed { get; set; } } } ================================================ FILE: IntelliTrader.Core/Models/Logging/MemorySink.cs ================================================ using System; using System.IO; using Serilog.Core; using Serilog.Events; using Serilog.Formatting; namespace IntelliTrader.Core { internal class MemorySink : ILogEventSink { readonly TextWriter textWriter; readonly ITextFormatter textFormatter; readonly object syncRoot = new object(); public MemorySink(System.IO.TextWriter textWriter, ITextFormatter textFormatter) { this.textWriter = textWriter; this.textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter)); } public void Emit(LogEvent logEvent) { if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); lock (syncRoot) { textFormatter.Format(logEvent, textWriter); textWriter.Flush(); } } } } ================================================ FILE: IntelliTrader.Core/Models/Logging/MemorySinkExtensions.cs ================================================ using System; using System.IO; using Serilog; using Serilog.Configuration; using Serilog.Core; using Serilog.Events; using Serilog.Formatting; using Serilog.Formatting.Display; namespace IntelliTrader.Core { /// /// Adds the WriteTo.Memory() extension method to . /// public static class TextWriterLoggerConfigurationExtensions { const string DefaultOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"; /// /// Write log events to the provided . /// /// Logger sink configuration. /// The text writer to write log events to. /// Message template describing the output format. /// The minimum level for /// events passed through the sink. Ignored when is specified. /// A switch allowing the pass-through minimum level /// to be changed at runtime. /// Configuration object allowing method chaining. /// Supplies culture-specific formatting information, or null. /// public static LoggerConfiguration Memory( this LoggerSinkConfiguration sinkConfiguration, TextWriter textWriter, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, string outputTemplate = DefaultOutputTemplate, IFormatProvider formatProvider = null, LoggingLevelSwitch levelSwitch = null) { if (textWriter == null) throw new ArgumentNullException(nameof(textWriter)); if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate)); var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); var sink = new MemorySink(textWriter, formatter); return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); } /// /// Write log events to the provided . /// /// Logger sink configuration. /// The text writer to write log events to. /// Text formatter used by sink. /// /// The minimum level for /// events passed through the sink. Ignored when is specified. /// A switch allowing the pass-through minimum level /// to be changed at runtime. /// public static LoggerConfiguration Memory( this LoggerSinkConfiguration sinkConfiguration, ITextFormatter formatter, TextWriter textWriter, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null) { if (textWriter == null) throw new ArgumentNullException(nameof(textWriter)); if (formatter == null) throw new ArgumentNullException(nameof(formatter)); var sink = new MemorySink(textWriter, formatter); return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); } } } ================================================ FILE: IntelliTrader.Core/Models/Tasks/EqualResolutionTimedTask.cs ================================================ using System; using System.Diagnostics; using System.Threading; namespace IntelliTrader.Core { public abstract class EqualResolutionTimedTask : ITimedTask { /// /// Raised on unhandled exception /// public event UnhandledExceptionEventHandler UnhandledException; /// /// Delay before starting the task in milliseconds /// public double StartDelay { get; set; } = 0; /// /// Periodic execution interval in milliseconds /// public double Interval { get; set; } = 1000; /// /// How often to skip task execution (in RunCount) /// public int SkipIteration { get; set; } = 0; /// /// The priority of the timer thread /// public ThreadPriority Priorty { get; set; } = ThreadPriority.Normal; /// /// Stopwatch to use for timing the intervals /// public Stopwatch Stopwatch { get; set; } /// /// Indicates whether the task is currently running /// public bool IsRunning { get; private set; } /// /// Number of times the task has been run /// public long RunCount { get; private set; } /// /// Total time it took to run the task in milliseconds /// public double TotalRunTime { get; private set; } /// /// Total task run delay in milliseconds /// public double TotalLagTime { get; private set; } private Thread timerThread; private Stopwatch runWatch; private ManualResetEvent timingEvent; private ManualResetEvent blockingEvent; /// /// Start the task /// public void Start() { if (!IsRunning) { IsRunning = true; runWatch = new Stopwatch(); timingEvent = new ManualResetEvent(false); blockingEvent = new ManualResetEvent(true); timerThread = new Thread(() => { if (Stopwatch == null) { Stopwatch = Stopwatch.StartNew(); } else if (!Stopwatch.IsRunning) { Stopwatch.Restart(); } long startTime = Stopwatch.ElapsedMilliseconds; while (IsRunning) { blockingEvent.WaitOne(); long elapsedTime = Stopwatch.ElapsedMilliseconds; double nextRunTime = RunCount * Interval + StartDelay + TotalLagTime + startTime; double waitTime = nextRunTime - elapsedTime; if (waitTime > 0) { if (timingEvent.WaitOne((int)(waitTime))) { break; } } if (SkipIteration == 0 || RunCount % SkipIteration != 0) { runWatch.Restart(); SafeRun(); long runTime = runWatch.ElapsedMilliseconds; TotalLagTime += (runTime > Interval) ? (runTime - Interval) : 0; TotalRunTime += runTime; } RunCount++; } }); timerThread.Priority = Priorty; timerThread.Start(); } } /// /// Stop the task /// public void Stop() { Stop(true); } /// /// Stop the task /// /// /// This function is waiting an executing thread (unless join is set to false). /// public void Stop(bool terminateThread) { if (IsRunning) { IsRunning = false; timingEvent.Set(); blockingEvent.Set(); runWatch.Stop(); if (!terminateThread) { timerThread?.Join(); timerThread = null; } timingEvent.Dispose(); } } /// /// Temporarily pause the task /// public void Pause() { blockingEvent.Reset(); } /// /// Continue running the task /// public void Continue() { blockingEvent.Set(); } /// /// Manually run the task /// public void RunNow() { SafeRun(); } /// /// This method must be implemented by the child class and must contain the code /// to be executed periodically. /// protected abstract void Run(); /// /// Wrap Run method in Try/Catch /// private void SafeRun() { try { Run(); } catch (Exception ex) { UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, false)); } } } } ================================================ FILE: IntelliTrader.Core/Models/Tasks/HighResolutionTimedTask.cs ================================================ using System; using System.Diagnostics; using System.Threading; namespace IntelliTrader.Core { public abstract class HighResolutionTimedTask : ITimedTask { /// /// Raised on unhandled exception /// public event UnhandledExceptionEventHandler UnhandledException; /// /// Delay before starting the task in milliseconds /// public double StartDelay { get; set; } = 0; /// /// Periodic execution interval in milliseconds /// public double Interval { get; set; } = 1000; /// /// How often to skip task execution (in RunCount) /// public int SkipIteration { get; set; } = 0; /// /// The priority of the timer thread /// public ThreadPriority Priorty { get; set; } = ThreadPriority.Normal; /// /// Stopwatch to use for timing the intervals /// public Stopwatch Stopwatch { get; set; } /// /// Indicates whether the task is currently running /// public bool IsRunning { get; private set; } /// /// Number of times the task has been run /// public long RunCount { get; private set; } /// /// Total time it took to run the task in milliseconds /// public double TotalRunTime { get; private set; } /// /// Total task run delay in milliseconds /// public double TotalLagTime { get; private set; } private Thread timerThread; private Stopwatch runWatch; private ManualResetEvent timingEvent; private ManualResetEvent blockingEvent; /// /// Start the task /// public void Start() { if (!IsRunning) { IsRunning = true; runWatch = new Stopwatch(); timingEvent = new ManualResetEvent(false); blockingEvent = new ManualResetEvent(true); timerThread = new Thread(() => { if (Stopwatch == null) { Stopwatch = Stopwatch.StartNew(); } else if (!Stopwatch.IsRunning) { Stopwatch.Restart(); } long startTime = Stopwatch.ElapsedMilliseconds; double nextRunTime = StartDelay + Interval; while (IsRunning) { blockingEvent.WaitOne(); long elapsedTime = Stopwatch.ElapsedMilliseconds; long runningTime = elapsedTime - startTime; if (nextRunTime < runningTime) nextRunTime = runningTime; double waitTime = nextRunTime - runningTime; if (waitTime > 0) { if (timingEvent.WaitOne((int)(waitTime))) { break; } } if (SkipIteration == 0 || RunCount % SkipIteration != 0) { runWatch.Restart(); SafeRun(); long runTime = runWatch.ElapsedMilliseconds; TotalLagTime += runTime - Interval; TotalRunTime += runTime; } RunCount++; nextRunTime += Interval; } }); timerThread.Priority = Priorty; timerThread.Start(); } } /// /// Stop the task /// public void Stop() { Stop(true); } /// /// Stop the task /// /// /// This function is waiting an executing thread (unless join is set to false). /// public void Stop(bool terminateThread) { if (IsRunning) { IsRunning = false; timingEvent.Set(); blockingEvent.Set(); runWatch.Stop(); if (!terminateThread) { timerThread?.Join(); timerThread = null; } timingEvent.Dispose(); } } /// /// Temporarily pause the task /// public void Pause() { blockingEvent.Reset(); } /// /// Continue running the task /// public void Continue() { blockingEvent.Set(); } /// /// Manually run the task /// public void RunNow() { SafeRun(); } /// /// This method must be implemented by the child class and must contain the code /// to be executed periodically. /// protected abstract void Run(); /// /// Wrap Run method in Try/Catch /// private void SafeRun() { try { Run(); } catch (Exception ex) { UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, false)); } } } } ================================================ FILE: IntelliTrader.Core/Models/Tasks/LowResolutionTimedTask.cs ================================================ using System; using System.Diagnostics; using System.Threading.Tasks; using System.Timers; namespace IntelliTrader.Core { public abstract class LowResolutionTimedTask : ITimedTask { /// /// Raised on unhandled exception /// #pragma warning disable CS0067 public event UnhandledExceptionEventHandler UnhandledException; /// /// Delay before starting the task in milliseconds /// public double StartDelay { get; set; } /// /// Periodic execution interval in milliseconds /// public double Interval { get { return timer.Interval; } set { timer.Interval = value; } } /// /// How often to skip task execution (in RunCount) /// public int SkipIteration { get; set; } = 0; /// /// Stopwatch to use for timing the intervals /// public Stopwatch Stopwatch { get; set; } /// /// Indicates whether the task is currently running /// public bool IsRunning { get; private set; } /// /// Number of times the task has been run /// public long RunCount { get; private set; } /// /// Total time it took to run the task in milliseconds /// public double TotalRunTime { get; private set; } /// /// Total task run delay in milliseconds /// public double TotalLagTime { get; private set; } private readonly Timer timer = new Timer(); private readonly Stopwatch runWatch = new Stopwatch(); private readonly System.Threading.AutoResetEvent syncMutex = new System.Threading.AutoResetEvent(true); /// /// Class constructor. It allocates the memory for the background timer and /// initializes sync mutex. /// public LowResolutionTimedTask() { this.timer.Elapsed += OnTimerElapsed; } /// /// Starts the background task timer that is in charge of executing the Execute method each /// time the interval is elapsed. /// public void Start() { if (!IsRunning) { IsRunning = true; if (StartDelay > 0) { Task.Delay((int)StartDelay).ContinueWith(t => { if (IsRunning) { timer.Start(); } }); } else { timer.Start(); } } } /// /// Stops the background task timer that is in charge of executing the Execute method each /// time the interval is elapsed. If the Execute method was executing when this method is /// called, the caller thread will block waiting the Execute operation to finish. Later on, /// the timer will be stopped. Otherwise, if the Execute method is not executing when this /// method is called, the timer will be stopped without blocking the caller thread. /// public void Stop() { Stop(-1); } /// /// Stops the background task timer that is in charge of executing the Execute method each /// time the interval is elapsed. If the Execute method was executing when this method is /// called, the caller thread will block waiting the Execute operation to finish. Later on, /// the timer will be stopped. Otherwise, if the Execute method is not executing when this /// method is called, the timer will be stopped without blocking the caller thread. /// /// Timeout value in milliseconds to wait before killing the task public void Stop(int timeout) { if (IsRunning) { this.syncMutex.WaitOne(timeout); this.syncMutex.Set(); this.timer.Stop(); IsRunning = false; } } /// /// Stops the periodic task executor without waiting the current task to stop. /// public void Terminate() { if (IsRunning) { this.timer.Stop(); IsRunning = false; } } /// /// This method can operate in two different ways. If the Execute method is currently executing, it will /// block the caller thread until Execute finishes. However, if the Execute method is not being executed, /// this method will not block and will immediately return back the control to the caller thread. /// public void Join() { if (IsRunning) { this.syncMutex.WaitOne(); this.syncMutex.Set(); } } /// /// Temporarily pause the task /// public void Pause() { this.timer.Enabled = false; } /// /// Continue running the task /// public void Continue() { this.timer.Enabled = true; } /// /// Wraps the Execute call abstracting the child class from the thread synchronization issues. /// /// The thimer object that is calling the event listener. /// The arguments passed by the timer to the method. private void OnTimerElapsed(object sender, ElapsedEventArgs e) { //Force other threads to wait until it's finished when calling join. this.syncMutex.Reset(); //Avoid re-calling the method while it is still operating. this.timer.Stop(); if (IsRunning) { runWatch.Restart(); this.Run(); long runTime = runWatch.ElapsedMilliseconds; TotalLagTime += (runTime > Interval) ? (runTime - Interval) : 0; TotalRunTime += runTime; RunCount++; runWatch.Stop(); //Re-Start the timer to execute the worker function endlessly. this.timer.Start(); } //Release threads that might be frozen in join operation. this.syncMutex.Set(); } /// /// Manually run the task /// public void RunNow() { Run(); } /// /// This method must be implemented by the child class and must contain the code /// to be executed periodically. /// protected abstract void Run(); } } ================================================ FILE: IntelliTrader.Core/Models/Trading/Arbitrage.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class Arbitrage { public bool IsAssigned { get; set; } public ArbitrageMarket Market { get; set; } public ArbitrageType Type { get; set; } public decimal Percentage { get; set; } } } ================================================ FILE: IntelliTrader.Core/Models/Trading/ArbitrageMarket.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum ArbitrageMarket { ETH, BNB, USDT } } ================================================ FILE: IntelliTrader.Core/Models/Trading/ArbitrageOptions.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class ArbitrageOptions { public string Pair { get; set; } public Arbitrage Arbitrage { get; set; } public bool ManualOrder { get; set; } public OrderMetadata Metadata { get; set; } public ArbitrageOptions(string pair, Arbitrage arbitrage, OrderMetadata newPairMetadata) { this.Pair = pair; this.Arbitrage = arbitrage; this.Metadata = newPairMetadata ?? new OrderMetadata(); } } } ================================================ FILE: IntelliTrader.Core/Models/Trading/ArbitrageType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum ArbitrageType { Direct, Reverse } } ================================================ FILE: IntelliTrader.Core/Models/Trading/BuyOptions.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class BuyOptions { public string Pair { get; set; } public decimal? Amount { get; set; } public decimal? MaxCost { get; set; } public decimal? Price { get; set; } public bool ManualOrder { get; set; } public bool Swap { get; set; } public bool Arbitrage { get; set; } public bool IgnoreExisting { get; set; } public bool IgnoreBalance { get; set; } public OrderMetadata Metadata { get; set; } public BuyOptions(string pair) { this.Pair = pair; this.Metadata = new OrderMetadata(); } } } ================================================ FILE: IntelliTrader.Core/Models/Trading/BuyTrailingStopAction.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum BuyTrailingStopAction { Buy, Cancel } } ================================================ FILE: IntelliTrader.Core/Models/Trading/DCALevel.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class DCALevel { public decimal Margin { get; set; } public decimal? BuyMultiplier { get; set; } public double? BuySamePairTimeout { get; set; } public decimal? BuyTrailing { get; set; } public decimal? BuyTrailingStopMargin { get; set; } public BuyTrailingStopAction? BuyTrailingStopAction { get; set; } public decimal? SellMargin { get; set; } public decimal? SellTrailing { get; set; } public decimal? SellTrailingStopMargin { get; set; } public SellTrailingStopAction? SellTrailingStopAction { get; set; } } } ================================================ FILE: IntelliTrader.Core/Models/Trading/OrderMetadata.cs ================================================ using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class OrderMetadata { public bool? IsTransitional { get; set; } public string OriginalPair { get; set; } public List TradingRules { get; set; } public string SignalRule { get; set; } public List Signals { get; set; } public double? BoughtRating { get; set; } public double? CurrentRating { get; set; } public double? BoughtGlobalRating { get; set; } public double? CurrentGlobalRating { get; set; } public decimal? LastBuyMargin { get; set; } public int? AdditionalDCALevels { get; set; } public decimal? AdditionalCosts { get; set; } public decimal? FeesNonDeductible { get; set; } public string SwapPair { get; set; } public string Arbitrage { get; set; } public decimal? ArbitragePercentage { get; set; } public OrderMetadata MergeWith(OrderMetadata metadata) { return new OrderMetadata { IsTransitional = metadata.IsTransitional ?? IsTransitional, OriginalPair = metadata.OriginalPair ?? OriginalPair, TradingRules = metadata.TradingRules ?? TradingRules, SignalRule = metadata.SignalRule ?? SignalRule, Signals = metadata.Signals ?? Signals, BoughtRating = metadata.BoughtRating ?? BoughtRating, CurrentRating = metadata.CurrentRating ?? CurrentRating, BoughtGlobalRating = metadata.BoughtGlobalRating ?? BoughtGlobalRating, CurrentGlobalRating = metadata.CurrentGlobalRating ?? CurrentGlobalRating, LastBuyMargin = metadata.LastBuyMargin ?? LastBuyMargin, AdditionalDCALevels = metadata.AdditionalDCALevels ?? AdditionalDCALevels, AdditionalCosts = metadata.AdditionalCosts ?? AdditionalCosts, FeesNonDeductible = metadata.FeesNonDeductible ?? FeesNonDeductible, SwapPair = metadata.SwapPair ?? SwapPair, Arbitrage = metadata.Arbitrage ?? Arbitrage, ArbitragePercentage = metadata.ArbitragePercentage ?? ArbitragePercentage }; } } } ================================================ FILE: IntelliTrader.Core/Models/Trading/OrderResult.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum OrderResult { Unknown = 0, Filled = 1, FilledPartially = 2, Pending = 3, Error = 4, Canceled = 5 } } ================================================ FILE: IntelliTrader.Core/Models/Trading/OrderSide.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum OrderSide { Buy, Sell } } ================================================ FILE: IntelliTrader.Core/Models/Trading/OrderType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum OrderType { Limit, Market } } ================================================ FILE: IntelliTrader.Core/Models/Trading/RuleAction.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum RuleAction { Default, Swap, Arbitrage } } ================================================ FILE: IntelliTrader.Core/Models/Trading/SellOptions.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class SellOptions { public string Pair { get; set; } public decimal? Amount { get; set; } public decimal? Price { get; set; } public bool ManualOrder { get; set; } public bool Swap { get; set; } public bool Arbitrage { get; set; } public OrderMetadata Metadata { get; set; } public SellOptions(string pair) { this.Pair = pair; this.Metadata = new OrderMetadata(); } } } ================================================ FILE: IntelliTrader.Core/Models/Trading/SellTrailingStopAction.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public enum SellTrailingStopAction { Sell, Cancel } } ================================================ FILE: IntelliTrader.Core/Models/Trading/SwapOptions.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public class SwapOptions { public string OldPair { get; set; } public string NewPair { get; set; } public bool ManualOrder { get; set; } public OrderMetadata Metadata { get; set; } public SwapOptions(string oldPair, string newPair, OrderMetadata newPairMetadata) { this.OldPair = oldPair; this.NewPair = newPair; this.Metadata = newPairMetadata ?? new OrderMetadata(); } } } ================================================ FILE: IntelliTrader.Core/Models/Trading/TradePriceType.cs ================================================ namespace IntelliTrader.Core { public enum TradePriceType { Last, Ask, Bid } } ================================================ FILE: IntelliTrader.Core/Models/Trading/TradeResult.cs ================================================ using IntelliTrader.Core; using Newtonsoft.Json; using System; using System.Collections.Generic; namespace IntelliTrader.Core { public class TradeResult : ITradeResult { public bool IsSuccessful { get; set; } public bool IsSwap { get; set; } public bool IsArbitrage { get; set; } public string Pair { get; set; } public decimal Amount { get; set; } public List OrderDates { get; set; } public decimal AveragePrice { get; set; } public decimal Fees { get; set; } public decimal FeesTotal => Fees + (Metadata?.FeesNonDeductible ?? 0); public decimal Cost => AveragePrice * Amount; public DateTimeOffset SellDate { get; set; } public decimal SellPrice { get; set; } public decimal SellCost => SellPrice * Amount; public decimal BalanceOffset { get; set; } public decimal Profit { get; set; } public OrderMetadata Metadata { get; set; } } } ================================================ FILE: IntelliTrader.Core/Models/Utils.cs ================================================ namespace IntelliTrader.Core { public static class Utils { public static decimal CalculatePercentage(decimal oldValue, decimal newValue) { if (oldValue != 0) { return (newValue - oldValue) / oldValue * 100; } else { return 0; } } } } ================================================ FILE: IntelliTrader.Core/Serialization/DecimalFormatJsonConverter.cs ================================================ using Newtonsoft.Json; using System; namespace IntelliTrader.Core { public class DecimalFormatJsonConverter : JsonConverter { private readonly int _numberOfDecimals; public DecimalFormatJsonConverter(int numberOfDecimals) { _numberOfDecimals = numberOfDecimals; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var d = (decimal)value; var rounded = Math.Round(d, _numberOfDecimals, MidpointRounding.AwayFromZero); writer.WriteValue(rounded); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter."); } public override bool CanRead { get { return false; } } public override bool CanConvert(Type objectType) { return objectType == typeof(decimal); } } } ================================================ FILE: IntelliTrader.Core/Services/ConfigurableServiceBase.cs ================================================ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Core { public abstract class ConfigrableServiceBase : IConfigurableService where TConfig : class { private const double DELAY_BETWEEN_CONFIG_RELOADS_MILLISECONDS = 500; public abstract string ServiceName { get; } public TConfig Config { get { lock (syncRoot) { if (config == null) { config = RawConfig.Get(); PrepareConfig(); } return config; } } } public IConfigurationSection RawConfig { get { lock (syncRoot) { if (rawConfig == null) { rawConfig = Application.ConfigProvider.GetSection(ServiceName, OnRawConfigChanged); } return rawConfig; } } } private TConfig config; private IConfigurationSection rawConfig; private DateTimeOffset lastReloadDate; private object syncRoot = new object(); protected virtual void PrepareConfig() { } protected virtual void OnConfigReloaded() { } private void OnRawConfigChanged(IConfigurationSection changedRawConfig) { lock (syncRoot) { rawConfig = changedRawConfig; config = null; } if ((DateTimeOffset.Now - lastReloadDate).TotalMilliseconds > DELAY_BETWEEN_CONFIG_RELOADS_MILLISECONDS) { lastReloadDate = DateTimeOffset.Now; PrepareConfig(); OnConfigReloaded(); Application.Resolve().Info($"{ServiceName} configuration reloaded"); } } } } ================================================ FILE: IntelliTrader.Core/Services/CoreService.cs ================================================ using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace IntelliTrader.Core { internal class CoreService : ConfigrableServiceBase, ICoreService { public event Action Started; public override string ServiceName => Constants.ServiceNames.CoreService; ICoreConfig ICoreService.Config => Config; public string Version { get; private set; } public string VersionType { get; private set; } = " Beta"; private readonly ILoggingService loggingService; private readonly ITasksService tasksService; private readonly INotificationService notificationService; private readonly IHealthCheckService healthCheckService; private readonly ITradingService tradingService; private readonly IWebService webService; private readonly IBacktestingService backtestingService; public CoreService(ILoggingService loggingService, ITasksService tasksService, INotificationService notificationService, IHealthCheckService healthCheckService, ITradingService tradingService, IWebService webService, IBacktestingService backtestingService) { this.loggingService = loggingService; this.tasksService = tasksService; this.notificationService = notificationService; this.healthCheckService = healthCheckService; this.tradingService = tradingService; this.webService = webService; this.backtestingService = backtestingService; // Log unhandled exceptions AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; tasksService.SetUnhandledExceptionHandler(OnUnhandledException); // Set decimal separator to a dot for all cultures var cultureInfo = new CultureInfo(CultureInfo.CurrentCulture.Name); cultureInfo.NumberFormat.NumberDecimalSeparator = "."; CultureInfo.DefaultThreadCurrentCulture = cultureInfo; CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; Version = GetType().Assembly.GetCustomAttribute().Version + VersionType; } public void Start() { loggingService.Info($"Start Core service (Version: {Version})..."); if (backtestingService.Config.Enabled) { backtestingService.Start(); } if (Config.HealthCheckInterval > 0 && (!backtestingService.Config.Enabled || !backtestingService.Config.Replay)) { healthCheckService.Start(); } if (tradingService.Config.Enabled) { tradingService.Start(); } if (notificationService.Config.Enabled) { notificationService.Start(); } if (webService.Config.Enabled) { webService.Start(); } ThreadPool.QueueUserWorkItem((state) => { Thread.Sleep(2000); Started?.Invoke(); tasksService.StartAllTasks(); }); loggingService.Info("Core service started"); notificationService.Notify($"IntelliTrader started"); } public void Stop() { notificationService.Notify("IntelliTrader stopped"); loggingService.Info("Stop Core service..."); if (tradingService.Config.Enabled) { tradingService.Stop(); } if (notificationService.Config.Enabled) { notificationService.Stop(); } if (webService.Config.Enabled) { webService.Stop(); } if (Config.HealthCheckInterval > 0 && (!backtestingService.Config.Enabled || !backtestingService.Config.Replay)) { healthCheckService.Stop(); } if (backtestingService.Config.Enabled) { backtestingService.Stop(); } tasksService.StopAllTasks(); tasksService.RemoveAllTasks(); loggingService.Info("Core service stopped"); } public void Restart() { notificationService.Notify("IntelliTrader restarting..."); loggingService.Info("Restart Core service..."); Task.Run(() => Stop()).Wait(TimeSpan.FromSeconds(20)); Start(); } private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { string message = "Unhandled exception occured"; if (e.ExceptionObject != null) { message = $"{message} - {e.ExceptionObject}"; } try { loggingService.Error(message); notificationService.Notify(message); } catch { } } } } ================================================ FILE: IntelliTrader.Core/Services/HealthCheckService.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; namespace IntelliTrader.Core { internal class HealthCheckService : IHealthCheckService { private readonly ILoggingService loggingService; private readonly INotificationService notificationService; private readonly ITasksService tasksService; private readonly ConcurrentDictionary healthChecks = new ConcurrentDictionary(); private HealthCheckTimedTask healthCheckTimedTask; public HealthCheckService(ILoggingService loggingService, INotificationService notificationService, ITasksService tasksService) { this.loggingService = loggingService; this.notificationService = notificationService; this.tasksService = tasksService; } public void Start() { loggingService.Info($"Start Health Check service..."); healthCheckTimedTask = tasksService.AddTask( name: nameof(HealthCheckTimedTask), task: new HealthCheckTimedTask(loggingService, notificationService, this, Application.Resolve(), Application.Resolve()), interval: Application.Resolve().Config.HealthCheckInterval * 1000 / Application.Speed, startDelay: Constants.TaskDelays.HighDelay, startTask: false, runNow: false, skipIteration: 0); loggingService.Info("Health Check service started"); } public void Stop() { loggingService.Info($"Stop Health Check service..."); tasksService.RemoveTask(nameof(HealthCheckTimedTask), stopTask: true); loggingService.Info("Health Check service stopped"); } public void UpdateHealthCheck(string name, string message = null, bool failed = false) { if (!healthChecks.TryGetValue(name, out HealthCheck existingHealthCheck)) { healthChecks.TryAdd(name, new HealthCheck { Name = name, Message = message, LastUpdated = DateTimeOffset.Now, Failed = failed }); } else { healthChecks[name].Message = message; healthChecks[name].LastUpdated = DateTimeOffset.Now; healthChecks[name].Failed = failed; } } public void RemoveHealthCheck(string name) { healthChecks.TryRemove(name, out HealthCheck healthCheck); } public IEnumerable GetHealthChecks() { foreach (var kvp in healthChecks) { yield return kvp.Value; } } } } ================================================ FILE: IntelliTrader.Core/Services/LoggingService.cs ================================================ using Microsoft.Extensions.Configuration; using Serilog; using Serilog.Core; using Serilog.Events; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace IntelliTrader.Core { internal class LoggingService : ConfigrableServiceBase, ILoggingService { private int LOG_ENTRIES_MAX_LENGTH = 50000; public override string ServiceName => Constants.ServiceNames.LoggingService; private Logger logger; private StringWriter writer; private StringBuilder writerStringBuilder; private string logsPath; private readonly object syncRoot = new object(); public LoggingService() { if (Config.Enabled) { logger = CreateLogger(); } } public void Verbose(string message, Exception exception = null) { lock (syncRoot) { if (Config.Enabled) { logger.Verbose(exception, message); CleanUpOldLogEntries(); } } } public void Verbose(string message, params object[] propertyValues) { lock (syncRoot) { if (Config.Enabled) { logger.Verbose(message, propertyValues); CleanUpOldLogEntries(); } } } public void Debug(string message, Exception exception = null) { lock (syncRoot) { if (Config.Enabled) { logger.Debug(exception, message); CleanUpOldLogEntries(); } } } public void Debug(string message, params object[] propertyValues) { lock (syncRoot) { if (Config.Enabled) { logger.Debug(message, propertyValues); CleanUpOldLogEntries(); } } } public void Info(string message, Exception exception = null) { lock (syncRoot) { if (Config.Enabled) { logger.Information(exception, message); CleanUpOldLogEntries(); } } } public void Info(string message, params object[] propertyValues) { lock (syncRoot) { if (Config.Enabled) { logger.Information(message, propertyValues); CleanUpOldLogEntries(); } } } public void Warning(string message, Exception exception = null) { lock (syncRoot) { if (Config.Enabled) { logger.Warning(exception, message); CleanUpOldLogEntries(); } } } public void Warning(string message, params object[] propertyValues) { lock (syncRoot) { if (Config.Enabled) { logger.Warning(message, propertyValues); CleanUpOldLogEntries(); } } } public void Error(string message, Exception exception = null) { lock (syncRoot) { if (Config.Enabled) { logger.Error(exception, message); CleanUpOldLogEntries(); } } } public void Error(string message, params object[] propertyValues) { lock (syncRoot) { if (Config.Enabled) { logger.Error(message, propertyValues); CleanUpOldLogEntries(); } } } public void Fatal(string message, Exception exception = null) { lock (syncRoot) { if (Config.Enabled) { logger.Fatal(exception, message); CleanUpOldLogEntries(); } } } public void Fatal(string message, params object[] propertyValues) { lock (syncRoot) { if (Config.Enabled) { logger.Fatal(message, propertyValues); CleanUpOldLogEntries(); } } } public void DeleteAllLogs() { lock(syncRoot) { logger.Dispose(); Directory.Delete(Path.Combine(Directory.GetCurrentDirectory(), logsPath), true); logger = CreateLogger(); } } public string[] GetLogEntries() { lock (syncRoot) { if (writer != null) { writer.Flush(); return writer.GetStringBuilder().ToString().Split(new string[] { writer.NewLine }, StringSplitOptions.RemoveEmptyEntries); } else { return new string[0]; } } } protected override void OnConfigReloaded() { lock (syncRoot) { logger = CreateLogger(); } } private Logger CreateLogger() { lock (syncRoot) { string outputTemplate = GetConfigValue("outputTemplate", RawConfig.GetChildren()); string filterExpression = GetConfigValue("expression", RawConfig.GetChildren()); string pathFormat = GetConfigValue("pathFormat", RawConfig.GetChildren()); logsPath = Path.GetDirectoryName(pathFormat); writerStringBuilder = new StringBuilder(); writer = new StringWriter(writerStringBuilder); return new LoggerConfiguration() .ReadFrom.ConfigurationSection(RawConfig) .WriteTo.Logger(config => config.WriteTo.Memory(writer, LogEventLevel.Information, outputTemplate).Filter.ByIncludingOnly(filterExpression)) .CreateLogger(); } } private void CleanUpOldLogEntries() { lock (syncRoot) { if (writerStringBuilder.Length > LOG_ENTRIES_MAX_LENGTH) { writerStringBuilder.Remove(0, writerStringBuilder.Length - LOG_ENTRIES_MAX_LENGTH); } } } private string GetConfigValue(string key, IEnumerable sections) { foreach (var section in sections) { if (section.Key == key) { return section.Value; } else { string value = GetConfigValue(key, section.GetChildren()); if (value != null) { return value; } } } return null; } } } ================================================ FILE: IntelliTrader.Core/Services/NotificationService.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; namespace IntelliTrader.Core { internal class NotificationService : ConfigrableServiceBase, INotificationService { public override string ServiceName => Constants.ServiceNames.NotificationService; INotificationConfig INotificationService.Config => Config; private readonly ILoggingService loggingService; // Telegram private TelegramBotClient telegramBotClient; private ChatId telegramChatId; public NotificationService(ILoggingService loggingService) { this.loggingService = loggingService; } public void Start() { try { loggingService.Info("Start Notification service..."); if (Config.TelegramEnabled) { telegramBotClient = new TelegramBotClient(Config.TelegramBotToken); var me = telegramBotClient.GetMeAsync().Result; telegramChatId = new ChatId(Config.TelegramChatId); } loggingService.Info("Notification service started"); } catch (Exception ex) { loggingService.Error("Unable to start Notification service", ex); Config.Enabled = false; } } public void Stop() { loggingService.Info("Stop Notification service..."); if (Config.TelegramEnabled) { telegramBotClient = null; } loggingService.Info("Notification service stopped"); } public void Notify(string message) { if (Config.Enabled) { if (Config.TelegramEnabled) { try { var instanceName = Application.Resolve().Config.InstanceName; telegramBotClient.SendTextMessageAsync(telegramChatId, $"({instanceName}) {message}", ParseMode.Default, false, !Config.TelegramAlertsEnabled); } catch (Exception ex) { loggingService.Error("Unable to send Telegram message", ex); } } } } protected override void OnConfigReloaded() { Stop(); Start(); } } } ================================================ FILE: IntelliTrader.Core/Services/TasksService.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace IntelliTrader.Core { internal class TasksService : ITasksService { private ConcurrentDictionary tasks = new ConcurrentDictionary(); private Stopwatch syncStopSwatch = new Stopwatch(); private UnhandledExceptionEventHandler exceptionHandler; public T AddTask(string name, T task, double interval, double startDelay = 0, bool startTask = true, bool runNow = false, int skipIteration = 0) where T : ITimedTask { tasks[name] = task; task.Interval = interval; task.StartDelay = startDelay; task.Stopwatch = syncStopSwatch; task.SkipIteration = skipIteration; if (exceptionHandler != null) { task.UnhandledException += exceptionHandler; } if (startTask) { task.Start(); } if (runNow) { task.RunNow(); } return task; } public void RemoveTask(string name, bool stopTask = true) { if (tasks.TryRemove(name, out ITimedTask task)) { if (stopTask) { task.Stop(); } if (exceptionHandler != null) { task.UnhandledException -= exceptionHandler; } task.Stopwatch = null; } } public void StartAllTasks() { foreach (var task in tasks.Values) { task.Start(); } } public void StopAllTasks() { foreach (var task in tasks.Values) { task.Stop(); } syncStopSwatch.Reset(); } public void RemoveAllTasks() { foreach (var taskName in tasks.Keys) { RemoveTask(taskName); } syncStopSwatch.Reset(); } public ITimedTask GetTask(string name) { if (tasks.TryGetValue(name, out ITimedTask task)) { return task; } else { return null; } } public T GetTask(string name) { return (T)GetTask(name); } public IEnumerable> GetAllTasks() { return tasks.AsEnumerable(); } public void SetUnhandledExceptionHandler(UnhandledExceptionEventHandler handler) { exceptionHandler = handler; } } } ================================================ FILE: IntelliTrader.Core/TimedTasks/HealthCheckTimedTask.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; namespace IntelliTrader.Core { internal class HealthCheckTimedTask : HighResolutionTimedTask { private readonly ILoggingService loggingService; private readonly INotificationService notificationService; private readonly IHealthCheckService healthCheckService; private readonly ICoreService coreService; private readonly ITradingService tradingService; private int healthCheckFailures = 0; public HealthCheckTimedTask(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ICoreService coreService, ITradingService tradingService) { this.loggingService = loggingService; this.notificationService = notificationService; this.healthCheckService = healthCheckService; this.coreService = coreService; this.tradingService = tradingService; } protected override void Run() { if (coreService.Config.HealthCheckEnabled) { bool healthCheckFailed = false; loggingService.Info("Health check results:"); foreach (var healthCheck in healthCheckService.GetHealthChecks().OrderBy(c => c.Name)) { var elapsedSinceLastUpdate = (DateTimeOffset.Now - healthCheck.LastUpdated).TotalSeconds; bool healthCheckTimeout = coreService.Config.HealthCheckSuspendTradingTimeout > 0 && elapsedSinceLastUpdate > coreService.Config.HealthCheckSuspendTradingTimeout; string indicator = (healthCheck.Failed || healthCheckTimeout) ? "[-]" : "[+]"; if (healthCheck.Message != null) { loggingService.Info($" {indicator} ({healthCheck.LastUpdated:HH:mm:ss}) {healthCheck.Name} - {healthCheck.Message}"); } else { loggingService.Info($" {indicator} ({healthCheck.LastUpdated:HH:mm:ss}) {healthCheck.Name}"); } if (healthCheck.Failed || healthCheckTimeout) { healthCheckFailed = true; } } if (healthCheckFailed) { healthCheckFailures++; } else { healthCheckFailures = 0; } if (healthCheckFailed && coreService.Config.HealthCheckFailuresToRestartServices > 0 && healthCheckFailures >= coreService.Config.HealthCheckFailuresToRestartServices) { coreService.Restart(); } else { if (healthCheckFailed) { loggingService.Info($"Health check failed ({healthCheckFailures})"); notificationService.Notify($"Health check failed ({healthCheckFailures})"); if (!tradingService.IsTradingSuspended) { healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingPairsProcessed); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingRulesProcessed); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.SignalRulesProcessed); tradingService.SuspendTrading(); } } else if (!healthCheckFailed && tradingService.IsTradingSuspended) { loggingService.Info("Health check passed"); notificationService.Notify("Health check passed"); tradingService.ResumeTrading(); } } } } } } ================================================ FILE: IntelliTrader.Exchange.Base/AppModule.cs ================================================ using Autofac; using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Exchange.Base { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { } } } ================================================ FILE: IntelliTrader.Exchange.Base/IntelliTrader.Exchange.Base.csproj ================================================ netcoreapp2.1 ================================================ FILE: IntelliTrader.Exchange.Base/Models/BuyOrder.cs ================================================ using System; using System.Collections.Generic; using System.Text; using IntelliTrader.Core; namespace IntelliTrader.Exchange.Base { public class BuyOrder : Order { public override OrderSide Side => OrderSide.Buy; } } ================================================ FILE: IntelliTrader.Exchange.Base/Models/Config/ExchangeConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Exchange.Base { public class ExchangeConfig { public string KeysPath { get; set; } public int RateLimitOccurences { get; set; } public int RateLimitTimeframe { get; set; } } } ================================================ FILE: IntelliTrader.Exchange.Base/Models/Order.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; namespace IntelliTrader.Exchange.Base { public abstract class Order : IOrder { public abstract OrderSide Side { get; } public OrderType Type { get; set; } public DateTimeOffset Date { get; set; } public string Pair { get; set; } public decimal Amount { get; set; } public decimal Price { get; set; } } } ================================================ FILE: IntelliTrader.Exchange.Base/Models/OrderDetails.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Exchange.Base { public class OrderDetails : IOrderDetails { public bool IsNormalized { get; set; } public OrderSide Side { get; set; } public OrderResult Result { get; set; } public DateTimeOffset Date { get; set; } public string OrderId { get; set; } public string Pair { get; set; } public string OriginalPair { get; set; } public string Message { get; set; } public decimal Amount { get; set; } public decimal AmountFilled { get; set; } public decimal Price { get; set; } public decimal AveragePrice { get; set; } public decimal Fees { get; set; } public string FeesCurrency { get; set; } public decimal Cost => AveragePrice * AmountFilled; public OrderMetadata Metadata { get; set; } = new OrderMetadata(); } } ================================================ FILE: IntelliTrader.Exchange.Base/Models/SellOrder.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Exchange.Base { public class SellOrder : Order { public override OrderSide Side => OrderSide.Sell; } } ================================================ FILE: IntelliTrader.Exchange.Base/Models/Ticker.cs ================================================ using IntelliTrader.Core; namespace IntelliTrader.Exchange.Base { public class Ticker : ITicker { public string Pair { get; set; } public decimal BidPrice { get; set; } public decimal AskPrice { get; set; } public decimal LastPrice { get; set; } } } ================================================ FILE: IntelliTrader.Exchange.Base/Services/ExchangeService.cs ================================================ using ExchangeSharp; using IntelliTrader.Core; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Exchange.Base { public abstract class ExchangeService : ConfigrableServiceBase, IExchangeService { public const int SOCKET_DISPOSE_TIMEOUT_MILLISECONDS = 10000; public const int MAX_TICKERS_AGE_TO_RECONNECT_MILLISECONDS = 60000; public const int INITIAL_TICKERS_TIMEOUT_MILLISECONDS = 5000; public const int INITIAL_TICKERS_RETRY_LIMIT = 4; public override string ServiceName => Constants.ServiceNames.ExchangeService; protected readonly ILoggingService loggingService; protected readonly IHealthCheckService healthCheckService; protected readonly ITasksService tasksService; public ExchangeAPI Api { get; set; } public ConcurrentDictionary Tickers { get; private set; } private IDisposable socket; private ConcurrentBag markets; private TickersMonitorTimedTask tickersMonitorTimedTask; private DateTimeOffset lastTickersUpdate; private bool tickersStarted; public ExchangeService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tasksService = tasksService; } public virtual void Start(bool virtualTrading) { loggingService.Info("Start Exchange service..."); Api = InitializeApi(); if (!virtualTrading && !String.IsNullOrWhiteSpace(Config.KeysPath)) { if (File.Exists(Config.KeysPath)) { loggingService.Info("Load keys from encrypted file..."); Api.LoadAPIKeys(Config.KeysPath); } else { throw new FileNotFoundException("Keys file not found"); } } loggingService.Info("Get initial ticker values..."); IEnumerable> exchangeTickers = null; for (int retry = 0; retry < INITIAL_TICKERS_RETRY_LIMIT; retry++) { Task.Run(() => exchangeTickers = Api.GetTickers()).Wait(TimeSpan.FromMilliseconds(INITIAL_TICKERS_TIMEOUT_MILLISECONDS)); if (exchangeTickers != null) break; } if (exchangeTickers != null) { Tickers = new ConcurrentDictionary(exchangeTickers.Select(t => new KeyValuePair(t.Key, new Ticker { Pair = t.Key, AskPrice = t.Value.Ask, BidPrice = t.Value.Bid, LastPrice = t.Value.Last }))); markets = new ConcurrentBag(Tickers.Keys.Select(pair => GetPairMarket(pair)).Distinct().ToList()); lastTickersUpdate = DateTimeOffset.Now; healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TickersUpdated, $"Updates: {Tickers.Count}"); } else if (Tickers != null) { loggingService.Error("Unable to get initial ticker values"); } else { throw new Exception("Unable to get initial ticker values"); } ConnectTickersWebsocket(); loggingService.Info("Exchange service started"); } public virtual void Stop() { loggingService.Info("Stop Exchange service..."); DisconnectTickersWebsocket(); lastTickersUpdate = DateTimeOffset.MinValue; healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TickersUpdated); loggingService.Info("Exchange service stopped"); } protected abstract ExchangeAPI InitializeApi(); public abstract IOrderDetails PlaceOrder(IOrder order); public virtual decimal ClampOrderAmount(string pair, decimal amount) { ExchangeMarket market = Api.GetExchangeMarketFromCache(pair); return market == null ? amount : CryptoUtility.ClampDecimal(market.MinTradeSize, market.MaxTradeSize, market.QuantityStepSize, amount); } public virtual decimal ClampOrderPrice(string pair, decimal price) { ExchangeMarket market = Api.GetExchangeMarketFromCache(pair); return market == null ? price : CryptoUtility.ClampDecimal(market.MinPrice, market.MaxPrice, market.PriceStepSize, price); } public void ConnectTickersWebsocket() { try { loggingService.Info("Connect to Exchange tickers..."); socket = Api.GetTickersWebSocket(OnTickersUpdated); loggingService.Info("Connected to Exchange tickers"); tickersMonitorTimedTask = tasksService.AddTask( name: nameof(TickersMonitorTimedTask), task: new TickersMonitorTimedTask(loggingService, this), interval: MAX_TICKERS_AGE_TO_RECONNECT_MILLISECONDS / 2, startDelay: Constants.TaskDelays.ZeroDelay, startTask: tickersStarted, runNow: false, skipIteration: 0); } catch (Exception ex) { loggingService.Error("Unable to connect to Exchange tickers", ex); } } public void DisconnectTickersWebsocket() { try { tasksService.RemoveTask(nameof(TickersMonitorTimedTask), stopTask: true); loggingService.Info("Disconnect from Exchange tickers..."); // Give Dispose 10 seconds to complete and then time out if not Task.Run(() => socket.Dispose()).Wait(TimeSpan.FromMilliseconds(SOCKET_DISPOSE_TIMEOUT_MILLISECONDS)); socket = null; loggingService.Info("Disconnected from Exchange tickers"); } catch (Exception ex) { loggingService.Error("Unable to disconnect from Exchange tickers", ex); } } public virtual IEnumerable GetTickers() { return Tickers.Values; } public virtual IEnumerable GetMarkets() { return markets.AsEnumerable(); } public virtual IEnumerable GetMarketPairs(string market) { return Tickers.Keys.Where(t => t.EndsWith(market)); } public virtual Dictionary GetAvailableAmounts() { return Api.GetAmountsAvailableToTradeAsync().Result; } public abstract IEnumerable GetTrades(string pair); public virtual decimal GetPrice(string pair, TradePriceType priceType) { if (Tickers.TryGetValue(pair, out Ticker ticker)) { if (priceType == TradePriceType.Ask) { return ticker.AskPrice; } else if (priceType == TradePriceType.Bid) { return ticker.BidPrice; } else { return ticker.LastPrice; } } else { return 0; } } public virtual decimal GetPriceSpread(string pair) { if (Tickers.TryGetValue(pair, out Ticker ticker)) { return Utils.CalculatePercentage(ticker.BidPrice, ticker.AskPrice); } else { return 0; } } public abstract Arbitrage GetArbitrage(string pair, string tradingMarket, List arbitrageMarkets = null, ArbitrageType? arbitrageType = null); public abstract string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket); public virtual string GetPairMarket(string pair) { return Api.ExchangeSymbolToGlobalSymbol(pair).Split('-')[0]; } public virtual string ChangeMarket(string pair, string market) { if (!pair.StartsWith(market) && !pair.EndsWith(market)) { string currentMarket = GetPairMarket(pair); return pair.Substring(0, pair.Length - currentMarket.Length) + market; } return pair; } public virtual decimal ConvertPrice(string pair, decimal price, string market, TradePriceType priceType) { string pairMarket = GetPairMarket(pair); if (pairMarket == Constants.Markets.USDT) { string marketPair = market + pairMarket; return price / GetPrice(marketPair, priceType); } else { string marketPair = pairMarket + market; return GetPrice(marketPair, priceType) * price; } } public TimeSpan GetTimeElapsedSinceLastTickersUpdate() { return DateTimeOffset.Now - lastTickersUpdate; } private void OnTickersUpdated(IReadOnlyCollection> updatedTickers) { if (!tickersStarted) { loggingService.Info("Ticker updates are working, good!"); tickersStarted = true; } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TickersUpdated, $"Updates: {updatedTickers.Count}"); lastTickersUpdate = DateTimeOffset.Now; foreach (var update in updatedTickers) { if (Tickers.TryGetValue(update.Key, out Ticker ticker)) { ticker.AskPrice = update.Value.Ask; ticker.BidPrice = update.Value.Bid; ticker.LastPrice = update.Value.Last; } else { Tickers.TryAdd(update.Key, new Ticker { Pair = update.Key, AskPrice = update.Value.Ask, BidPrice = update.Value.Bid, LastPrice = update.Value.Last }); var market = GetPairMarket(update.Key); if (!markets.Contains(market)) { markets.Add(market); } } } } } } ================================================ FILE: IntelliTrader.Exchange.Base/TimedTasks/TickersMonitorTimedTask.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Exchange.Base; namespace IntelliTrader.Exchange { internal class TickersMonitorTimedTask : HighResolutionTimedTask { private readonly ILoggingService loggingService; private readonly IExchangeService exchangeService; public TickersMonitorTimedTask(ILoggingService loggingService, IExchangeService exchangeService) { this.loggingService = loggingService; this.exchangeService = exchangeService; } protected override void Run() { if (exchangeService.GetTimeElapsedSinceLastTickersUpdate().TotalMilliseconds > ExchangeService.MAX_TICKERS_AGE_TO_RECONNECT_MILLISECONDS) { loggingService.Info("Exchange max tickers age reached, reconnecting..."); exchangeService.DisconnectTickersWebsocket(); exchangeService.ConnectTickersWebsocket(); } } } } ================================================ FILE: IntelliTrader.Exchange.Binance/AppModule.cs ================================================ using Autofac; using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Exchange.Binance { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().Named("Binance").As().Named("ExchangeBinance").SingleInstance().PreserveExistingDefaults(); } } } ================================================ FILE: IntelliTrader.Exchange.Binance/BinanceExchangeService.cs ================================================ using ExchangeSharp; using IntelliTrader.Core; using IntelliTrader.Exchange.Base; using System; using System.Linq; using System.Collections.Generic; namespace IntelliTrader.Exchange.Binance { internal class BinanceExchangeService : ExchangeService { public BinanceExchangeService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService) : base(loggingService, healthCheckService, tasksService) { } protected override ExchangeAPI InitializeApi() { var binanceApi = new ExchangeBinanceAPI { RateLimit = new RateGate(Config.RateLimitOccurences, TimeSpan.FromSeconds(Config.RateLimitTimeframe)) }; return binanceApi; } public override IOrderDetails PlaceOrder(IOrder order) { var result = Api.PlaceOrderAsync(new ExchangeOrderRequest { OrderType = (ExchangeSharp.OrderType)(int)order.Type, IsBuy = order.Side == OrderSide.Buy, Amount = order.Amount, Price = order.Price, Symbol = order.Pair }).Result; return new OrderDetails { Side = result.IsBuy ? OrderSide.Buy : OrderSide.Sell, Result = (OrderResult)(int)result.Result, Date = result.OrderDate, OrderId = result.OrderId, Pair = result.Symbol, Message = result.Message, Amount = result.Amount, AmountFilled = result.AmountFilled, Price = result.Price, AveragePrice = result.AveragePrice, Fees = result.Fees, FeesCurrency = result.FeesCurrency }; } public override IEnumerable GetTrades(string pair) { var myTrades = new List(); var results = ((ExchangeBinanceAPI)Api).GetMyTrades(pair); foreach (var result in results) { myTrades.Add(new OrderDetails { Side = result.IsBuy ? OrderSide.Buy : OrderSide.Sell, Result = (OrderResult)(int)result.Result, Date = result.OrderDate, OrderId = result.OrderId, Pair = result.Symbol, Message = result.Message, Amount = result.Amount, AmountFilled = result.AmountFilled, Price = result.Price, AveragePrice = result.AveragePrice, Fees = result.Fees, FeesCurrency = result.FeesCurrency }); } return myTrades; } public override Arbitrage GetArbitrage(string pair, string tradingMarket, List arbitrageMarkets = null, ArbitrageType? arbitrageType = null) { if (arbitrageMarkets == null || !arbitrageMarkets.Any()) { arbitrageMarkets = new List { ArbitrageMarket.ETH, ArbitrageMarket.BNB, ArbitrageMarket.USDT }; } Arbitrage arbitrage = new Arbitrage { Market = arbitrageMarkets.First(), Type = arbitrageType ?? ArbitrageType.Direct }; try { if (tradingMarket == Constants.Markets.BTC) { foreach (var market in arbitrageMarkets) { string marketPair = ChangeMarket(pair, market.ToString()); string arbitragePair = GetArbitrageMarketPair(market); if (marketPair != pair && Tickers.TryGetValue(pair, out Ticker pairTicker) && Tickers.TryGetValue(marketPair, out Ticker marketTicker) && Tickers.TryGetValue(arbitragePair, out Ticker arbitrageTicker)) { decimal directArbitragePercentage = 0; decimal reverseArbitragePercentage = 0; if (market == ArbitrageMarket.ETH) { directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100; reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100; } else if (market == ArbitrageMarket.BNB) { directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice * arbitrageTicker.BidPrice - 1) * 100; reverseArbitragePercentage = (1 / arbitrageTicker.AskPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100; } else if (market == ArbitrageMarket.USDT) { directArbitragePercentage = (1 / pairTicker.AskPrice * marketTicker.BidPrice / arbitrageTicker.AskPrice - 1) * 100; reverseArbitragePercentage = (arbitrageTicker.BidPrice / marketTicker.AskPrice * pairTicker.BidPrice - 1) * 100; } if ((directArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Direct)) { arbitrage.IsAssigned = true; arbitrage.Market = market; arbitrage.Type = ArbitrageType.Direct; arbitrage.Percentage = directArbitragePercentage; } if ((reverseArbitragePercentage > arbitrage.Percentage || !arbitrage.IsAssigned) && (arbitrageType == null || arbitrageType == ArbitrageType.Reverse)) { arbitrage.IsAssigned = true; arbitrage.Market = market; arbitrage.Type = ArbitrageType.Reverse; arbitrage.Percentage = reverseArbitragePercentage; } } } } } catch { } return arbitrage; } public override string GetArbitrageMarketPair(ArbitrageMarket arbitrageMarket) { if (arbitrageMarket == ArbitrageMarket.ETH) { return Constants.Markets.ETH + Constants.Markets.BTC; } else if (arbitrageMarket == ArbitrageMarket.BNB) { return Constants.Markets.BNB + Constants.Markets.BTC; } else if (arbitrageMarket == ArbitrageMarket.USDT) { return Constants.Markets.BTC + Constants.Markets.USDT; } else { throw new NotSupportedException($"Unsupported arbitrage market: {arbitrageMarket}"); } } } } ================================================ FILE: IntelliTrader.Exchange.Binance/IntelliTrader.Exchange.Binance.csproj ================================================ netcoreapp2.1 ================================================ FILE: IntelliTrader.Launcher/IntelliTrader.Launcher.csproj ================================================  Debug AnyCPU {B458FDA9-0E55-40BB-86C9-711F1FA18CDF} WinExe IntelliTrader.Launcher IntelliTrader v4.0 512 Client publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true AnyCPU false none false ..\IntelliTrader\ DEBUG;TRACE prompt 4 false AnyCPU none true ..\IntelliTrader\ TRACE prompt 4 false IntelliTrader.ico IntelliTrader.Launcher.Program true False .NET Framework 3.5 SP1 true ================================================ FILE: IntelliTrader.Launcher/Program.cs ================================================ using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; namespace IntelliTrader.Launcher { class Program { const int PROCESS_WAIT_TIMEOUT = 1; [DllImport("user32.dll")] static extern int SetWindowText(IntPtr hWnd, string text); static void Main(string[] args) { string instanceName = args.Length == 1 ? args[0] : null; string processFileName = $"{nameof(IntelliTrader)}.dll"; string processPath = Directory.EnumerateFiles(Directory.GetCurrentDirectory(), processFileName, SearchOption.AllDirectories).FirstOrDefault(); if (!string.IsNullOrWhiteSpace(processPath)) { Process process = new Process(); try { process.StartInfo.FileName = "dotnet"; process.StartInfo.Arguments = $"\"{processPath}\""; process.StartInfo.WindowStyle = ProcessWindowStyle.Normal; process.Start(); Thread.Sleep(TimeSpan.FromSeconds(PROCESS_WAIT_TIMEOUT)); if (process.HasExited) { 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)); return; } SpinWait.SpinUntil(() => { return process.MainWindowHandle != IntPtr.Zero; }, TimeSpan.FromSeconds(PROCESS_WAIT_TIMEOUT)); } catch (Exception ex) { 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)); } try { if (!String.IsNullOrWhiteSpace(instanceName)) { SetWindowText(process.MainWindowHandle, $"{nameof(IntelliTrader)} - {instanceName}"); } else { SetWindowText(process.MainWindowHandle, $"{nameof(IntelliTrader)}"); } } catch { } } else { MessageBox.Show($"{processFileName} not found", nameof(IntelliTrader)); } } } } ================================================ FILE: IntelliTrader.Launcher/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("IntelliTrader.Launcher")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("IntelliTrader.Launcher")] [assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("b458fda9-0e55-40bb-86c9-711f1fa18cdf")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: IntelliTrader.Rules/AppModule.cs ================================================ using Autofac; using IntelliTrader.Core; using System; namespace IntelliTrader.Rules { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().As().Named(Constants.ServiceNames.RulesService).SingleInstance(); } } } ================================================ FILE: IntelliTrader.Rules/Config/RulesConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Rules { internal class RulesConfig : IRulesConfig { public IEnumerable Modules { get; set; } IEnumerable IRulesConfig.Modules => Modules; } } ================================================ FILE: IntelliTrader.Rules/IntelliTrader.Rules.csproj ================================================ netcoreapp2.1 ================================================ FILE: IntelliTrader.Rules/Models/ModuleRules.cs ================================================ using IntelliTrader.Core; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Rules { internal class ModuleRules : IModuleRules { public string Module { get; set; } public IConfigurationSection Configuration { get; set; } public IEnumerable Entries { get; set; } IEnumerable IModuleRules.Entries => Entries; public T GetConfiguration() { return Configuration.Get(); } } } ================================================ FILE: IntelliTrader.Rules/Models/Rule.cs ================================================ using IntelliTrader.Core; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Rules { internal class Rule : IRule { public bool Enabled { get; set; } public string Name { get; set; } public RuleAction Action { get; set; } public IEnumerable Conditions { get; set; } public RuleTrailing Trailing { get; set; } public IConfigurationSection Modifiers { get; set; } IEnumerable IRule.Conditions => Conditions; IRuleTrailing IRule.Trailing => Trailing; private object typedModifiersCached; public T GetModifiers() { if (typedModifiersCached == null) { typedModifiersCached = Modifiers.Get(); } return (T)typedModifiersCached; } } } ================================================ FILE: IntelliTrader.Rules/Models/RuleCondition.cs ================================================ using IntelliTrader.Core; using System.Collections.Generic; namespace IntelliTrader.Rules { internal class RuleCondition : IRuleCondition { public string Signal { get; set; } public decimal? MinPrice { get; set; } public decimal? MaxPrice { get; set; } public decimal? MinSpread { get; set; } public decimal? MaxSpread { get; set; } public long? MinVolume { get; set; } public long? MaxVolume { get; set; } public double? MinVolumeChange { get; set; } public double? MaxVolumeChange { get; set; } public decimal? MinPriceChange { get; set; } public decimal? MaxPriceChange { get; set; } public double? MinRating { get; set; } public double? MaxRating { get; set; } public double? MinRatingChange { get; set; } public double? MaxRatingChange { get; set; } public double? MinVolatility { get; set; } public double? MaxVolatility { get; set; } public double? MinGlobalRating { get; set; } public double? MaxGlobalRating { get; set; } public decimal? MinArbitrage { get; set; } public decimal? MaxArbitrage { get; set; } public ArbitrageMarket? ArbitrageMarket { get; set; } public ArbitrageType? ArbitrageType { get; set; } public List Pairs { get; set; } public List NotPairs { get; set; } // Trading pair specific conditions public double? MinAge { get; set; } public double? MaxAge { get; set; } public double? MinLastBuyAge { get; set; } public double? MaxLastBuyAge { get; set; } public decimal? MinMargin { get; set; } public decimal? MaxMargin { get; set; } public decimal? MinMarginChange { get; set; } public decimal? MaxMarginChange { get; set; } public decimal? MinAmount { get; set; } public decimal? MaxAmount { get; set; } public decimal? MinCost { get; set; } public decimal? MaxCost { get; set; } public int? MinDCALevel { get; set; } public int? MaxDCALevel { get; set; } public List SignalRules { get; set; } public List NotSignalRules { get; set; } } } ================================================ FILE: IntelliTrader.Rules/Models/RuleTrailing.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Rules { internal class RuleTrailing : IRuleTrailing { public bool Enabled { get; set; } public int MinDuration { get; set; } public int MaxDuration { get; set; } public IEnumerable StartConditions { get; set; } IEnumerable IRuleTrailing.StartConditions => StartConditions; } } ================================================ FILE: IntelliTrader.Rules/Services/RulesService.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace IntelliTrader.Rules { internal class RulesService : ConfigrableServiceBase, IRulesService { public override string ServiceName => Constants.ServiceNames.RulesService; IRulesConfig IRulesService.Config => Config; private readonly ILoggingService loggingService; private readonly ITradingService tradingService; private readonly List rulesChangeCallbacks = new List(); public RulesService(ILoggingService loggingService, ITradingService tradingService) { this.loggingService = loggingService; this.tradingService = tradingService; } public IModuleRules GetRules(string module) { IModuleRules moduleRules = Config.Modules.FirstOrDefault(m => m.Module == module); if (moduleRules != null) { return moduleRules; } else { throw new Exception($"Unable to find rules for {module}"); } } public bool CheckConditions(IEnumerable conditions, Dictionary signals, double? globalRating, string pair, ITradingPair tradingPair) { if (conditions != null) { foreach (var condition in conditions) { ISignal signal = null; if (condition.Signal != null && signals.TryGetValue(condition.Signal, out ISignal s)) { signal = s; } if (condition.MinPrice != null && (tradingService.GetPrice(pair) < condition.MinPrice) || condition.MaxPrice != null && (tradingService.GetPrice(pair) > condition.MaxPrice) || condition.MinSpread != null && (tradingService.Exchange.GetPriceSpread(pair) < condition.MinSpread) || condition.MaxSpread != null && (tradingService.Exchange.GetPriceSpread(pair) > condition.MaxSpread) || condition.MinArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, condition.ArbitrageMarket != null ? new List { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage < condition.MinArbitrage || condition.MaxArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, condition.ArbitrageMarket != null ? new List { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage > condition.MaxArbitrage || condition.MinVolume != null && (signal == null || signal.Volume == null || signal.Volume < condition.MinVolume) || condition.MaxVolume != null && (signal == null || signal.Volume == null || signal.Volume > condition.MaxVolume) || condition.MinVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange < condition.MinVolumeChange) || condition.MaxVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange > condition.MaxVolumeChange) || condition.MinPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange < condition.MinPriceChange) || condition.MaxPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange > condition.MaxPriceChange) || condition.MinRating != null && (signal == null || signal.Rating == null || signal.Rating < condition.MinRating) || condition.MaxRating != null && (signal == null || signal.Rating == null || signal.Rating > condition.MaxRating) || condition.MinRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange < condition.MinRatingChange) || condition.MaxRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange > condition.MaxRatingChange) || condition.MinVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility < condition.MinVolatility) || condition.MaxVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility > condition.MaxVolatility) || condition.MinGlobalRating != null && (globalRating == null || globalRating < condition.MinGlobalRating) || condition.MaxGlobalRating != null && (globalRating == null || globalRating > condition.MaxGlobalRating) || condition.Pairs != null && (pair == null || !condition.Pairs.Contains(pair)) || condition.NotPairs != null && (pair == null || condition.NotPairs.Contains(pair)) || condition.MinAge != null && (tradingPair == null || tradingPair.CurrentAge < condition.MinAge / Application.Speed) || condition.MaxAge != null && (tradingPair == null || tradingPair.CurrentAge > condition.MaxAge / Application.Speed) || condition.MinLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge < condition.MinLastBuyAge / Application.Speed) || condition.MaxLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge > condition.MaxLastBuyAge / Application.Speed) || condition.MinMargin != null && (tradingPair == null || tradingPair.CurrentMargin < condition.MinMargin) || condition.MaxMargin != null && (tradingPair == null || tradingPair.CurrentMargin > condition.MaxMargin) || condition.MinMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) < condition.MinMarginChange) || condition.MaxMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) > condition.MaxMarginChange) || condition.MinAmount != null && (tradingPair == null || tradingPair.Amount < condition.MinAmount) || condition.MaxAmount != null && (tradingPair == null || tradingPair.Amount > condition.MaxAmount) || condition.MinCost != null && (tradingPair == null || tradingPair.CurrentCost < condition.MinCost) || condition.MaxCost != null && (tradingPair == null || tradingPair.CurrentCost > condition.MaxCost) || condition.MinDCALevel != null && (tradingPair == null || tradingPair.DCALevel < condition.MinDCALevel) || condition.MaxDCALevel != null && (tradingPair == null || tradingPair.DCALevel > condition.MaxDCALevel) || condition.SignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || !condition.SignalRules.Contains(tradingPair.Metadata.SignalRule)) || condition.NotSignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || condition.NotSignalRules.Contains(tradingPair.Metadata.SignalRule))) { return false; } } } return true; } public void RegisterRulesChangeCallback(Action callback) { rulesChangeCallbacks.Add(callback); } public void UnregisterRulesChangeCallback(Action callback) { rulesChangeCallbacks.Remove(callback); } protected override void OnConfigReloaded() { foreach (var callback in rulesChangeCallbacks) { callback(); } } } } ================================================ FILE: IntelliTrader.Signals.Base/AppModule.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Autofac; using IntelliTrader.Core; namespace IntelliTrader.Signals.Base { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().As().Named(Constants.ServiceNames.SignalsService).SingleInstance().PreserveExistingDefaults(); } } } ================================================ FILE: IntelliTrader.Signals.Base/IntelliTrader.Signals.Base.csproj ================================================ netcoreapp2.1 ================================================ FILE: IntelliTrader.Signals.Base/Interfaces/ISignaReceiver.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Signals.Base { public interface ISignalReceiver { string SignalName { get; } void Start(); void Stop(); int GetPeriod(); IEnumerable GetSignals(); double? GetAverageRating(); } } ================================================ FILE: IntelliTrader.Signals.Base/Models/Config/SignalsConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Signals.Base { public class SignalsConfig : ISignalsConfig { public bool Enabled { get; set; } public IEnumerable GlobalRatingSignals { get; set; } public IEnumerable Definitions { get; set; } IEnumerable ISignalsConfig.Definitions => Definitions; } } ================================================ FILE: IntelliTrader.Signals.Base/Models/Signal.cs ================================================ using IntelliTrader.Core; namespace IntelliTrader.Signals.Base { public class Signal : ISignal { public string Name { get; set; } public string Pair { get; set; } public long? Volume { get; set; } public double? VolumeChange { get; set; } public decimal? Price { get; set; } public decimal? PriceChange { get; set; } public double? Rating { get; set; } public double? RatingChange { get; set; } public double? Volatility { get; set; } } } ================================================ FILE: IntelliTrader.Signals.Base/Models/SignalDefinition.cs ================================================ using IntelliTrader.Core; using Microsoft.Extensions.Configuration; namespace IntelliTrader.Signals.Base { public class SignalDefinition : ISignalDefinition { public string Name { get; set; } public string Receiver { get; set; } public IConfigurationSection Configuration { get; set; } } } ================================================ FILE: IntelliTrader.Signals.Base/Models/SignalRuleModifiers.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Signals.Base { internal class SignalRuleModifiers { public decimal? CostMultiplier { get; set; } } } ================================================ FILE: IntelliTrader.Signals.Base/Models/SignalRulesConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Signals.Base { public class SignalRulesConfig : ISignalRulesConfig { public RuleProcessingMode ProcessingMode { get; set; } public double CheckInterval { get; set; } } } ================================================ FILE: IntelliTrader.Signals.Base/Models/SignalTrailingInfo.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Signals.Base { internal class SignalTrailingInfo : ISignalTrailingInfo { public IRule Rule { get; set; } public DateTimeOffset StartTime { get; set; } public double Duration => (DateTimeOffset.Now - StartTime).TotalSeconds; } } ================================================ FILE: IntelliTrader.Signals.Base/Services/SignalsService.cs ================================================ using Autofac; using IntelliTrader.Core; using Microsoft.Extensions.Configuration; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; namespace IntelliTrader.Signals.Base { public class SignalsService : ConfigrableServiceBase, ISignalsService { public override string ServiceName => Constants.ServiceNames.SignalsService; ISignalsConfig ISignalsService.Config => Config; public IModuleRules Rules { get; private set; } public ISignalRulesConfig RulesConfig { get; private set; } private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITasksService tasksService; private readonly ITradingService tradingService; private readonly IRulesService rulesService; private ConcurrentDictionary signalReceivers = new ConcurrentDictionary(); private SignalRulesTimedTask signalRulesTimedTask; public SignalsService(ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService, ITradingService tradingService, IRulesService rulesService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tasksService = tasksService; this.tradingService = tradingService; this.rulesService = rulesService; } public void Start() { loggingService.Info("Start Signals service..."); OnSignalRulesChanged(); rulesService.RegisterRulesChangeCallback(OnSignalRulesChanged); signalReceivers.Clear(); foreach (var definition in Config.Definitions) { var receiver = Application.ResolveOptionalNamed(definition.Receiver, new TypedParameter(typeof(string), definition.Name), new TypedParameter(typeof(IConfigurationSection), definition.Configuration)); if (receiver != null) { if (signalReceivers.TryAdd(definition.Name, receiver)) { receiver.Start(); } else { throw new Exception($"Duplicate signal definition: {definition.Name}"); } } else { throw new Exception($"Signal receiver not found: {definition.Receiver}"); } } signalRulesTimedTask = tasksService.AddTask( name: nameof(SignalRulesTimedTask), task: new SignalRulesTimedTask(loggingService, healthCheckService, tradingService, rulesService, this), interval: RulesConfig.CheckInterval * 1000 / Application.Speed, startDelay: Constants.TaskDelays.LowDelay, startTask: false, runNow: false, skipIteration: 0); loggingService.Info("Signals service started"); } public void Stop() { loggingService.Info("Stop Signals service..."); foreach (var receiver in signalReceivers.Values) { receiver.Stop(); } signalReceivers.Clear(); tasksService.RemoveTask(nameof(SignalRulesTimedTask), stopTask: true); rulesService.UnregisterRulesChangeCallback(OnSignalRulesChanged); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.SignalRulesProcessed); loggingService.Info("Signals service stopped"); } public void ProcessPair(string pair, Dictionary signals) { IEnumerable enabledRules = Rules.Entries.Where(r => r.Enabled); foreach (IRule rule in enabledRules) { signalRulesTimedTask.ProcessRule(rule, signals, pair, signalRulesTimedTask.GetExcludedPairs(), GetGlobalRating()); } } public void StopTrailing() { signalRulesTimedTask.StopTrailing(); } public List GetTrailingSignals() { return signalRulesTimedTask.GetTrailingSignals(); } public IEnumerable GetTrailingInfo(string pair) { return signalRulesTimedTask.GetTrailingInfo(pair); } public IEnumerable GetSignalNames() { return signalReceivers.OrderBy(r => r.Value.GetPeriod()).Select(r => r.Key); } public IEnumerable GetAllSignals() { return GetSignalsByName(null); } public IEnumerable GetSignalsByName(string signalName) { IEnumerable signals = null; foreach (var kvp in signalReceivers.OrderBy(r => r.Value.GetPeriod())) { if (signalName == null || signalName == kvp.Key) { ISignalReceiver receiver = kvp.Value; if (signals == null) { signals = receiver.GetSignals(); } else { signals = signals.Concat(receiver.GetSignals()); } } } return signals; } public IEnumerable GetSignalsByPair(string pair) { foreach (var receiver in signalReceivers.Values.OrderBy(r => r.GetPeriod())) { var signal = receiver.GetSignals().FirstOrDefault(s => s.Pair == pair); if (signal != null) { yield return signal; } } } public ISignal GetSignal(string pair, string signalName) { return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair); } public double? GetRating(string pair, string signalName) { return GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating; } public double? GetRating(string pair, IEnumerable signalNames) { if (signalNames != null && signalNames.Count() > 0) { double ratingSum = 0; foreach (var signalName in signalNames) { var rating = GetSignalsByName(signalName)?.FirstOrDefault(s => s.Pair == pair)?.Rating; if (rating != null) { ratingSum += rating.Value; } else { return null; } } return Math.Round(ratingSum / signalNames.Count(), 8); } else { return null; } } public double? GetGlobalRating() { try { double ratingSum = 0; double ratingCount = 0; foreach (var kvp in signalReceivers) { string signalName = kvp.Key; if (Config.GlobalRatingSignals.Contains(signalName)) { ISignalReceiver receiver = kvp.Value; double? averageRating = receiver.GetAverageRating(); if (averageRating != null) { ratingSum += averageRating.Value; ratingCount++; } } } if (ratingCount > 0) { return Math.Round(ratingSum / ratingCount, 8); } else { return null; } } catch (Exception ex) { loggingService.Error("Unable to get global rating", ex); return null; } } private void OnSignalRulesChanged() { Rules = rulesService.GetRules(ServiceName); RulesConfig = Rules.GetConfiguration(); } } } ================================================ FILE: IntelliTrader.Signals.Base/TimedTasks/SignalRulesTimedTask.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace IntelliTrader.Signals.Base { public class SignalRulesTimedTask : HighResolutionTimedTask { public bool LoggingEnabled { get; set; } = true; private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITradingService tradingService; private readonly IRulesService rulesService; private readonly ISignalsService signalsService; private ConcurrentDictionary> trailingSignals = new ConcurrentDictionary>(); public SignalRulesTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService, IRulesService rulesService, ISignalsService signalsService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tradingService = tradingService; this.rulesService = rulesService; this.signalsService = signalsService; } protected override void Run() { ProcessTrailingSignals(); ProcessAllRules(); } public void StopTrailing() { trailingSignals.Clear(); } public void StopTrailing(string pair) { trailingSignals.TryRemove(pair, out List trailingInfo); } public List GetTrailingSignals() { return trailingSignals.Keys.ToList(); } public IEnumerable GetTrailingInfo(string pair) { if (trailingSignals.TryGetValue(pair, out List trailingInfo)) { return trailingInfo; } else { return null; } } private void ProcessTrailingSignals() { double? globalRating = signalsService.GetGlobalRating(); foreach (var kvp in trailingSignals) { string pair = kvp.Key; List trailingInfoList = kvp.Value; for (int i = trailingInfoList.Count - 1; i >= 0; i--) { SignalTrailingInfo trailingInfo = trailingInfoList[i]; if (trailingInfo.Rule.Trailing.MaxDuration == 0 || trailingInfo.Duration <= trailingInfo.Rule.Trailing.MaxDuration / Application.Speed) { if (trailingInfo.Duration >= trailingInfo.Rule.Trailing.MinDuration / Application.Speed) { IEnumerable signalsByPair = signalsService.GetSignalsByPair(pair); if (signalsByPair != null) { Dictionary signals = signalsByPair.ToDictionary(s => s.Name, s => s); if (rulesService.CheckConditions(trailingInfo.Rule.Conditions, signals, globalRating, pair, null)) { IEnumerable ruleSignals = signals.Where(s => trailingInfo.Rule.Conditions.Any(c => c.Signal == s.Key)).Select(s => s.Value); InitiateBuy(pair, trailingInfo.Rule, ruleSignals); } } } } else { trailingInfoList.RemoveAt(i); if (trailingInfoList.Count == 0) { StopTrailing(pair); } if (LoggingEnabled) { loggingService.Info($"Cancel trailing signal for {pair}. Rule: {trailingInfo.Rule.Name}, Reason: max duration reached"); } } } } } public void ProcessAllRules() { if (tradingService.Config.BuyEnabled) { IEnumerable allSignals = signalsService.GetAllSignals(); if (allSignals != null) { IEnumerable enabledRules = signalsService.Rules.Entries.Where(r => r.Enabled); if (enabledRules.Any()) { 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)); double? globalRating = signalsService.GetGlobalRating(); List excludedPairs = GetExcludedPairs(); if (signalsService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch) { excludedPairs.AddRange(trailingSignals.Keys); } foreach (IRule rule in enabledRules) { foreach (var group in groupedSignals) { Dictionary signals = group.Value; ProcessRule(rule, signals, group.Key, excludedPairs, globalRating); } } } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.SignalRulesProcessed, $"Rules: {enabledRules.Count()}, Trailing signals: {trailingSignals.Count}"); } } } public void ProcessRule(IRule rule, Dictionary signals, string pair, List excludedPairs, double? globalRating) { IEnumerable conditions = rule.Trailing != null && rule.Trailing.Enabled ? rule.Trailing.StartConditions : rule.Conditions; ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair, includeDust: true); List trailingInfoList; if (!excludedPairs.Contains(pair) && (!trailingSignals.TryGetValue(pair, out trailingInfoList) || !trailingInfoList.Any(t => t.Rule == rule)) && (conditions == null || rulesService.CheckConditions(conditions, signals, globalRating, pair, tradingPair))) { IEnumerable ruleSignals = conditions != null ? signals.Where(s => conditions.Any(c => c.Signal == s.Key)).Select(s => s.Value) : new List(); if (rule.Trailing != null && rule.Trailing.Enabled) { if (trailingInfoList == null) { trailingInfoList = new List(); trailingSignals.TryAdd(pair, trailingInfoList); } trailingInfoList.Add(new SignalTrailingInfo { Rule = rule, StartTime = DateTimeOffset.Now }); if (LoggingEnabled) { loggingService.Info($"Start trailing signal for {pair}. Rule: {rule.Name}"); } } else { InitiateBuy(pair, rule, ruleSignals); } if (signalsService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch) { excludedPairs.Add(pair); } } } public List GetExcludedPairs() { return tradingService.Config.ExcludedPairs .Concat(tradingService.Account.GetTradingPairs().Select(p => p.Pair)) .Concat(tradingService.GetTrailingBuys()).ToList(); } private void InitiateBuy(string pair, IRule rule, IEnumerable ruleSignals) { StopTrailing(pair); IPairConfig pairConfig = tradingService.GetPairConfig(pair); SignalRuleModifiers ruleModifiers = rule.GetModifiers(); if (LoggingEnabled) { loggingService.Info($"Initiate buy request for {pair}. Rule: {rule.Name}"); } var buyOptions = new BuyOptions(pair) { MaxCost = pairConfig.BuyMaxCost * pairConfig.BuyMultiplier * (ruleModifiers?.CostMultiplier ?? 1), Metadata = new OrderMetadata { SignalRule = rule.Name, Signals = ruleSignals.Select(s => s.Name).ToList(), BoughtRating = ruleSignals.Any(s => s.Rating != null) ? ruleSignals.Where(s => s.Rating != null).Average(s => s.Rating) : null, BoughtGlobalRating = signalsService.GetGlobalRating() } }; tradingService.Buy(buyOptions); } } } ================================================ FILE: IntelliTrader.Signals.TradingView/AppModule.cs ================================================ using Autofac; using IntelliTrader.Core; using IntelliTrader.Signals.Base; namespace IntelliTrader.Signals.TradingView { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().Named(nameof(TradingViewCryptoSignalReceiver)); } } } ================================================ FILE: IntelliTrader.Signals.TradingView/IntelliTrader.Signals.TradingView.csproj ================================================ netcoreapp2.1 ================================================ FILE: IntelliTrader.Signals.TradingView/Models/Config/TradingViewCryptoSignalReceiverConfig.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Signals.TradingView { internal class TradingViewCryptoSignalReceiverConfig { public double PollingInterval { get; set; } public int SignalPeriod { get; set; } public string VolatilityPeriod { get; set; } public string RequestUrl { get; set; } public string RequestData { get; set; } } } ================================================ FILE: IntelliTrader.Signals.TradingView/Models/TradingViewCryptoSignalConverter.cs ================================================ using IntelliTrader.Signals.Base; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Linq; namespace IntelliTrader.Signals.TradingView { internal class TradingViewCryptoSignalConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(Signal); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.StartArray) { var array = JArray.Load(reader); var item = (existingValue as Signal ?? new Signal()); item.Pair = (string)array.ElementAtOrDefault(0); item.Price = (decimal?)array.ElementAtOrDefault(1); item.PriceChange = (decimal?)array.ElementAtOrDefault(2); item.Volume = (long?)array.ElementAtOrDefault(3); item.Rating = (double?)array.ElementAtOrDefault(4); item.Volatility = (double?)array.ElementAtOrDefault(5); return item; } else { return null; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotSupportedException(); } } } ================================================ FILE: IntelliTrader.Signals.TradingView/Receivers/TradingViewCryptoSignalReceiver.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Signals.Base; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; namespace IntelliTrader.Signals.TradingView { internal class TradingViewCryptoSignalReceiver : ISignalReceiver { public string SignalName { get; private set; } public TradingViewCryptoSignalReceiverConfig Config { get; private set; } private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITasksService tasksService; private readonly ISignalsService signalsService; private readonly ITradingService tradingService; private TradingViewCryptoSignalPollingTimedTask tradingViewCryptoSignalPollingTimedTask; public TradingViewCryptoSignalReceiver(string signalName, IConfigurationSection configuration, ILoggingService loggingService, IHealthCheckService healthCheckService, ITasksService tasksService, ISignalsService signalsService, ITradingService tradingService) { this.SignalName = signalName; this.Config = configuration.Get(); this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tasksService = tasksService; this.signalsService = signalsService; this.tradingService = tradingService; } public void Start() { loggingService.Info("Start TradingViewCryptoSignalReceiver..."); tradingViewCryptoSignalPollingTimedTask = tasksService.AddTask( name: $"{nameof(TradingViewCryptoSignalPollingTimedTask)} [{SignalName}]", task: new TradingViewCryptoSignalPollingTimedTask(loggingService, healthCheckService, tradingService, this), interval: Config.PollingInterval * 1000 / Application.Speed, startDelay: Constants.TaskDelays.ZeroDelay, startTask: false, runNow: true, skipIteration: 0); loggingService.Info("TradingViewCryptoSignalReceiver started"); } public void Stop() { loggingService.Info("Stop TradingViewCryptoSignalReceiver..."); tasksService.RemoveTask($"{nameof(TradingViewCryptoSignalPollingTimedTask)} [{SignalName}]", stopTask: true); healthCheckService.RemoveHealthCheck($"{Constants.HealthChecks.TradingViewCryptoSignalsReceived} [{SignalName}]"); loggingService.Info("TradingViewCryptoSignalReceiver stopped"); } public int GetPeriod() { return Config.SignalPeriod; } public IEnumerable GetSignals() { return tradingViewCryptoSignalPollingTimedTask?.GetSignals() ?? new List(); } public double? GetAverageRating() { return tradingViewCryptoSignalPollingTimedTask?.GetAverageRating(); } } } ================================================ FILE: IntelliTrader.Signals.TradingView/TimedTasks/TradingViewCryptoSignalPollingTimedTask.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Signals.Base; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text; namespace IntelliTrader.Signals.TradingView { internal class TradingViewCryptoSignalPollingTimedTask : HighResolutionTimedTask { private const int HISTORICAL_SIGNALS_SNAPSHOT_MIN_INTERVAL_MILLISECONDS = 45000; private const int HISTORICAL_SIGNALS_ADDITIONAL_SAVE_MINUTES = 5; private const int HISTORICAL_SIGNALS_MAX_ADDITIONAL_ELAPSED_MINUTES = 1; private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITradingService tradingService; private readonly TradingViewCryptoSignalReceiver signalReceiver; private readonly JsonSerializer signalsSerializer; private readonly HttpClient httpClient; private readonly ConcurrentDictionary> signalsHistory = new ConcurrentDictionary>(); private DateTimeOffset lastSnapshotDate; private List signals; private double? averageRating; private object syncRoot = new object(); public TradingViewCryptoSignalPollingTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService, TradingViewCryptoSignalReceiver signalReceiver) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tradingService = tradingService; this.signalReceiver = signalReceiver; this.signalsSerializer = new JsonSerializer(); this.signalsSerializer.Converters.Add(new TradingViewCryptoSignalConverter()); this.httpClient = CreateHttpClient(); } protected override void Run() { var requestData = signalReceiver.Config.RequestData .Replace("%EXCHANGE%", tradingService.Config.Exchange.ToUpper()) .Replace("%MARKET%", tradingService.Config.Market) .Replace("%PERIOD%", signalReceiver.Config.SignalPeriod <= 240 ? $"|{signalReceiver.Config.SignalPeriod}" : "") .Replace("%VOLATILITY%", $".{signalReceiver.Config.VolatilityPeriod?[0] ?? 'W'}"); var requestContent = new StringContent(requestData, Encoding.UTF8, "application/json"); try { using (var response = httpClient.PostAsync(signalReceiver.Config.RequestUrl, requestContent).Result) { var responseContent = response.Content.ReadAsStringAsync().Result; var jtokens = JObject.Parse(responseContent).SelectTokens("data[*].d"); lock (syncRoot) { List historicalSignals = GetHistoricalSignals(); signals = jtokens.Select(t => { try { var signal = t.ToObject(signalsSerializer); if (signal.Pair.EndsWith(tradingService.Config.Market)) { signal.Name = signalReceiver.SignalName; var historicalSignal = historicalSignals?.FirstOrDefault(s => s.Pair == signal.Pair); if (historicalSignal != null) { signal.VolumeChange = CalculatePercentageChange(historicalSignal.Volume, signal.Volume); signal.RatingChange = CalculatePercentageChange(historicalSignal.Rating, signal.Rating); } return signal; } else { return null; } } catch (Exception ex) { loggingService.Debug("Unable to parse Trading View Crypto Signal", ex); return null; } }).Where(s => s != null && s.Pair != null).ToList(); if (signals.Count > 0) { if ((DateTimeOffset.Now - lastSnapshotDate).TotalMilliseconds > HISTORICAL_SIGNALS_SNAPSHOT_MIN_INTERVAL_MILLISECONDS) { signalsHistory.TryAdd(DateTimeOffset.Now, signals); lastSnapshotDate = DateTimeOffset.Now; CleanUpSignalsHistory(); } averageRating = signals.Any(s => s.Rating != null) ? signals.Where(s => s.Rating != null).Average(s => s.Rating) : null; healthCheckService.UpdateHealthCheck($"{Constants.HealthChecks.TradingViewCryptoSignalsReceived} [{signalReceiver.SignalName}]", $"Total: {signals.Count()}"); } } } } catch (Exception ex) { loggingService.Debug("Unable to retrieve TV Signals", ex); } } public IEnumerable GetSignals() { lock (syncRoot) { return signals; } } public double? GetAverageRating() { lock (syncRoot) { return averageRating; } } private List GetHistoricalSignals() { lock (syncRoot) { foreach (var date in signalsHistory.Keys.OrderByDescending(d => d)) { double elapsedMinutes = (DateTimeOffset.Now - date).TotalMinutes; if (elapsedMinutes >= signalReceiver.Config.SignalPeriod && (elapsedMinutes - signalReceiver.Config.SignalPeriod) <= HISTORICAL_SIGNALS_MAX_ADDITIONAL_ELAPSED_MINUTES) { return signalsHistory[date]; } } return null; } } private void CleanUpSignalsHistory() { lock (syncRoot) { foreach (var date in signalsHistory.Keys) { if ((DateTimeOffset.Now - date).TotalMinutes > signalReceiver.Config.SignalPeriod + HISTORICAL_SIGNALS_ADDITIONAL_SAVE_MINUTES) { signalsHistory.TryRemove(date, out List signals); } } } } private double? CalculatePercentageChange(double? a, double? b) { if (a != null && b != null) { if (a == 0 && b == 0 || a == b) { return 0; } else if (a == 0) { return 100 * Math.Sign((double)b); } else if (b == 0) { return -100 * Math.Sign((double)a); } else { var change = Math.Abs((double)((b - a) / a * 100)); return (a < b) ? change : change * -1; } } else { return null; } } private HttpClient CreateHttpClient() { var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Accept.Clear(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); return httpClient; } } } ================================================ FILE: IntelliTrader.Trading/AppModule.cs ================================================ using Autofac; using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Trading { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().As().Named(Constants.ServiceNames.TradingService).SingleInstance(); builder.RegisterType().As().SingleInstance(); } } } ================================================ FILE: IntelliTrader.Trading/IntelliTrader.Trading.csproj ================================================  netcoreapp2.1 ================================================ FILE: IntelliTrader.Trading/Models/Accounts/ExchangeAccount.cs ================================================ using IntelliTrader.Core; using Newtonsoft.Json; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; namespace IntelliTrader.Trading { internal class ExchangeAccount : TradingAccountBase { private const string BACKUP_DIR = "backup"; public override bool IsVirtual => false; public ExchangeAccount(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ISignalsService signalsService, ITradingService tradingService) : base(loggingService, notificationService, healthCheckService, signalsService, tradingService) { } public override void Refresh() { loggingService.Info("Refresh account..."); decimal newBalance = 0; Dictionary availableAmounts = new Dictionary(); Dictionary> availableTrades = new Dictionary>(); DateTimeOffset refreshStart = DateTimeOffset.Now; // Preload account data without locking the account try { loggingService.Info("Get account data..."); foreach (var kvp in tradingService.Exchange.GetAvailableAmounts()) { string currency = kvp.Key; decimal amount = kvp.Value; string pair = currency + tradingService.Config.Market; if (currency == tradingService.Config.Market) { newBalance = amount; } else if (!tradingService.Config.ExcludedPairs.Contains(pair)) { try { IEnumerable trades = tradingService.Exchange.GetTrades(pair); availableTrades.Add(pair, trades); availableAmounts.Add(pair, amount); } catch (Exception ex) when (ex.Message != null && ex.Message.Contains("Invalid symbol")) { loggingService.Info($"Skip invalid pair: {pair}"); } } } loggingService.Info("Account data retrieved"); } catch (Exception ex) when (!isInitialRefresh) { healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, ex.Message, true); loggingService.Error("Unable to get account data", ex); notificationService.Notify("Unable to get account data"); return; } // Lock the account and reapply all trades try { lock (SyncRoot) { ConcurrentDictionary tradingPairsSaved = null; if (isInitialRefresh) { TradingAccountData data = LoadSavedData(); tradingPairsSaved = data?.TradingPairs ?? new ConcurrentDictionary(); } else { tradingPairsSaved = tradingPairs; } tradingPairs = new ConcurrentDictionary(); foreach (var kvp in availableTrades) { string pair = kvp.Key; decimal amount = availableAmounts[pair]; IEnumerable trades = kvp.Value; foreach (var trade in trades) { if (trade.Date >= tradingService.Config.AccountInitialBalanceDate) { if (trade.Side == OrderSide.Buy) { AddBuyOrder(trade); } else { ITradeResult tradeResult = AddSellOrder(trade); } if (isInitialRefresh) { tradingService.LogOrder(trade); } } } if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && tradingPair.Amount != amount) { loggingService.Info($"Adjust amount for {pair}: {tradingPair.Amount:0.########} => {amount:0.########}"); tradingPair.Amount = amount; } } foreach (var pair in tradingPairs.Keys.ToList()) { if (tradingPairsSaved.TryGetValue(pair, out TradingPair saved)) { tradingPairs[pair].Metadata = saved.Metadata ?? new OrderMetadata(); } } balance = newBalance; // Add trades that were completed during account refresh foreach (var order in tradingService.OrderHistory) { if (order.Date > refreshStart) { if (tradingPairs.TryGetValue(order.Pair, out TradingPair tradingPair)) { if (!tradingPair.OrderIds.Contains(order.OrderId)) { loggingService.Info($"Add missing order for {order.Pair} ({order.OrderId})"); AddOrder(order); } } else { loggingService.Info($"Add missing order for {order.Pair} ({order.OrderId})"); AddOrder(order); } } } if (isInitialRefresh) { isInitialRefresh = false; } loggingService.Info($"Account refreshed. Balance: {balance}, Trading pairs: {tradingPairs.Count}"); healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, $"Balance: {balance}, Trading pairs: {tradingPairs.Count}"); } } catch (Exception ex) { tradingPairs.Clear(); tradingService.SuspendTrading(); healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, ex.Message, true); loggingService.Error("Unable to refresh account", ex); notificationService.Notify("Unable to refresh account"); } } public override void Save() { lock (SyncRoot) { try { var tradingService = Application.Resolve(); string accountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.AccountFilePath); var data = new TradingAccountData { Balance = balance, TradingPairs = tradingPairs, }; string accountJson = JsonConvert.SerializeObject(data, Formatting.Indented); var accountFile = new FileInfo(accountFilePath); accountFile.Directory.Create(); File.WriteAllText(accountFile.FullName, accountJson); } catch (Exception ex) { loggingService.Error("Unable to save account data", ex); } } } private TradingAccountData LoadSavedData() { lock (SyncRoot) { try { var tradingService = Application.Resolve(); string accountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.AccountFilePath); if (File.Exists(accountFilePath)) { BackupAccountData(accountFilePath); string accountJson = File.ReadAllText(accountFilePath); return JsonConvert.DeserializeObject(accountJson); } else { return null; } } catch (Exception ex) { loggingService.Error("Unable to load account data", ex); return null; } } } private void BackupAccountData(string accountFilePath) { try { string backupAccountFileName = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd-HH-mm-ss") + ".json"; string backupAccountFilePath = Path.Combine(Path.GetDirectoryName(accountFilePath), BACKUP_DIR, backupAccountFileName); var backupAccountFile = new FileInfo(backupAccountFilePath); backupAccountFile.Directory.Create(); File.Copy(accountFilePath, backupAccountFile.FullName); } catch (Exception ex) { loggingService.Error("Unable to save account backup data", ex); } } public override void Dispose() { base.Dispose(); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.AccountRefreshed); } } } ================================================ FILE: IntelliTrader.Trading/Models/Accounts/TradingAccountBase.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Exchange.Base; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; namespace IntelliTrader.Trading { internal abstract class TradingAccountBase : ITradingAccount { public object SyncRoot { get; private set; } = new object(); public abstract bool IsVirtual { get; } protected readonly ILoggingService loggingService; protected readonly INotificationService notificationService; protected readonly IHealthCheckService healthCheckService; protected readonly ISignalsService signalsService; protected readonly ITradingService tradingService; protected bool isInitialRefresh = true; protected decimal balance; protected ConcurrentDictionary tradingPairs = new ConcurrentDictionary(); public TradingAccountBase(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ISignalsService signalsService, ITradingService tradingService) { this.loggingService = loggingService; this.notificationService = notificationService; this.healthCheckService = healthCheckService; this.signalsService = signalsService; this.tradingService = tradingService; } public abstract void Refresh(); public abstract void Save(); public virtual void AddOrder(IOrderDetails order) { if (order.Side == OrderSide.Buy) { AddBuyOrder(order); } else { AddSellOrder(order); } } public virtual void AddBuyOrder(IOrderDetails order) { lock (SyncRoot) { if (order.Side == OrderSide.Buy && (order.Result == OrderResult.Filled || order.Result == OrderResult.FilledPartially)) { string feesPair = order.FeesCurrency + tradingService.Config.Market; decimal feesPairCurrency = (feesPair == order.Pair) ? order.Fees : 0; decimal feesMarketCurrency = tradingService.CalculateOrderFees(order); decimal balanceOffset = -feesMarketCurrency; if (!order.IsNormalized || order.Pair.EndsWith(Constants.Markets.USDT)) { balanceOffset -= order.Cost; AddBalance(balanceOffset); } else { string normalizedMarket = tradingService.Exchange.GetPairMarket(order.OriginalPair) == Constants.Markets.USDT ? tradingService.Config.Market + tradingService.Exchange.GetPairMarket(order.OriginalPair) : tradingService.Exchange.GetPairMarket(order.OriginalPair) + tradingService.Config.Market; if (tradingPairs.TryGetValue(normalizedMarket, out TradingPair normalizedMarketPair)) { if (normalizedMarketPair.Cost > order.Cost) { decimal amount = order.Cost / tradingService.GetPrice(normalizedMarket, TradePriceType.Bid); normalizedMarketPair.Amount -= amount; if (normalizedMarketPair.Amount <= 0) { tradingPairs.TryRemove(normalizedMarket, out normalizedMarketPair); if (normalizedMarketPair.Amount < 0) { loggingService.Error($"Normalized pair {normalizedMarket} has negative amount: {normalizedMarketPair.Amount}"); } } } else { tradingPairs.TryRemove(normalizedMarket, out normalizedMarketPair); } } else { loggingService.Error($"Unable to get normalized pair {normalizedMarketPair}"); } } AddOrUpdatePair(order, order.Pair, feesMarketCurrency, feesPairCurrency); } } } public virtual ITradeResult AddSellOrder(IOrderDetails order) { ITradeResult tradeResult = new TradeResult(); lock (SyncRoot) { if (tradingPairs.TryGetValue(order.Pair, out TradingPair tradingPair)) { if (order.Side == OrderSide.Sell && (order.Result == OrderResult.Filled || order.Result == OrderResult.FilledPartially)) { string feesPair = order.FeesCurrency + tradingService.Config.Market; decimal feesPairCurrency = (feesPair == order.Pair) ? order.Fees : 0; decimal feesMarketCurrency = tradingService.CalculateOrderFees(order); decimal amountDifference = order.AmountFilled / tradingPair.Amount; decimal balanceOffset = -feesMarketCurrency; if (!order.IsNormalized || order.Pair.EndsWith(Constants.Markets.USDT)) { balanceOffset += order.Cost; AddBalance(balanceOffset); } else { string normalizedMarket = tradingService.Exchange.GetPairMarket(order.OriginalPair) == Constants.Markets.USDT ? tradingService.Config.Market + tradingService.Exchange.GetPairMarket(order.OriginalPair) : tradingService.Exchange.GetPairMarket(order.OriginalPair) + tradingService.Config.Market; decimal price = tradingService.GetPrice(normalizedMarket, TradePriceType.Ask); decimal amount = (order.Cost - feesMarketCurrency) / price; AddOrUpdatePair(order, normalizedMarket, feesMarketCurrency, feesPairCurrency, amount, price); } decimal sellFees = feesMarketCurrency + tradingPair.Fees * amountDifference; tradingPair.Fees += feesMarketCurrency - sellFees; decimal costDifference = order.Cost - tradingPair.GetPartialCost(order.AmountFilled) - (tradingPair.Metadata.AdditionalCosts ?? 0); decimal profit = costDifference * amountDifference - feesMarketCurrency; if (tradingPair.Amount > order.AmountFilled) { tradingPair.Amount -= order.AmountFilled; if (tradingPair.CurrentCost < tradingService.Config.MinCost) { tradingPair.OrderDates.Clear(); } } else { tradingPairs.TryRemove(order.Pair, out tradingPair); } tradeResult = new TradeResult { IsSuccessful = true, Pair = order.Pair, Amount = order.AmountFilled, OrderDates = tradingPair.OrderDates, AveragePrice = tradingPair.AveragePrice, Fees = sellFees, SellDate = order.Date, SellPrice = order.AveragePrice, BalanceOffset = balanceOffset, Profit = profit, Metadata = order.Metadata, }; } } } return tradeResult; } public ITradingPair AddOrUpdatePair(IOrderDetails order, string pair, decimal feesMarketCurrency, decimal feesPairCurrency, decimal? amountOverride = null, decimal? averagePriceOverride = null) { decimal amount = amountOverride ?? order.AmountFilled; decimal amountAfterFees = amount - feesPairCurrency; decimal averagePrice = averagePriceOverride ?? (order.AveragePrice + (feesMarketCurrency / amount)); if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair)) { if (!tradingPair.OrderIds.Contains(order.OrderId)) { tradingPair.OrderIds.Add(order.OrderId); tradingPair.OrderDates.Add(order.Date); } tradingPair.AveragePrice = (tradingPair.Cost + amountAfterFees * averagePrice) / (tradingPair.Amount + amountAfterFees); tradingPair.Amount += amountAfterFees; tradingPair.Fees += feesMarketCurrency; tradingPair.SetMetadata(tradingPair.Metadata.MergeWith(order.Metadata)); } else { tradingPair = new TradingPair { Pair = pair, OrderIds = new List { order.OrderId }, OrderDates = new List { order.Date }, AveragePrice = averagePrice, Amount = amountAfterFees, Fees = feesMarketCurrency, Metadata = order.Metadata }; tradingPairs.TryAdd(pair, tradingPair); tradingPair.SetCurrentValues(tradingService.GetPrice(tradingPair.Pair), tradingService.Exchange.GetPriceSpread(tradingPair.Pair)); tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null ? signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null; tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating(); if (tradingPair.Metadata.LastBuyMargin == null) { tradingPair.Metadata.LastBuyMargin = tradingPair.CurrentMargin; } } return tradingPair; } public IOrderDetails AddBlankOrder(string pair, decimal amount, bool includeFees = true) { lock (SyncRoot) { if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && tradingPair.Amount >= amount) { if (tradingPair.Amount > amount) { return new OrderDetails { OrderId = DateTime.Now.ToFileTimeUtc().ToString(), Side = OrderSide.Sell, Result = OrderResult.Filled, Date = DateTimeOffset.Now, Pair = pair, Amount = amount, AmountFilled = amount, Price = tradingPair.AveragePrice, AveragePrice = tradingPair.AveragePrice, Fees = includeFees ? tradingPair.Fees * (amount / tradingPair.Amount) : 0, FeesCurrency = includeFees ? tradingService.Config.Market : null, Metadata = tradingPair.Metadata }; } else { return new OrderDetails(); } } else { return new OrderDetails(); } } } public void AddBalance(decimal balanceOffset) { balance += balanceOffset; } public decimal GetBalance() { lock (SyncRoot) { return balance; } } public decimal GetTotalBalance() { decimal totalBalance = balance; foreach (var tradingPair in tradingPairs.Values) { totalBalance += tradingService.GetPrice(tradingPair.Pair, TradePriceType.Bid) * tradingPair.Amount; } return totalBalance; } public bool HasTradingPair(string pair, bool includeDust = false) { lock (SyncRoot) { if (includeDust) { return tradingPairs.ContainsKey(pair); } else { return tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && (tradingPair.CurrentCost > tradingService.Config.MinCost || tradingPair.CurrentPrice == 0); } } } public ITradingPair GetTradingPair(string pair, bool includeDust = false) { lock (SyncRoot) { if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && (includeDust || tradingPair.CurrentCost > tradingService.Config.MinCost || tradingPair.CurrentPrice == 0)) { return tradingPair; } else { return null; } } } public IEnumerable GetTradingPairs(bool includeDust = false) { lock (SyncRoot) { if (includeDust) { return tradingPairs.Values; } else { return tradingPairs.Values.Where(t => t.CurrentCost > tradingService.Config.MinCost || t.CurrentPrice == 0); } } } public virtual void Dispose() { lock (SyncRoot) { tradingPairs.Clear(); } } } } ================================================ FILE: IntelliTrader.Trading/Models/Accounts/TradingAccountData.cs ================================================ using IntelliTrader.Core; using Newtonsoft.Json; using System.Collections.Concurrent; namespace IntelliTrader.Trading { internal class TradingAccountData { [JsonConverter(typeof(DecimalFormatJsonConverter), 8)] public decimal Balance { get; set; } public ConcurrentDictionary TradingPairs { get; set; } } } ================================================ FILE: IntelliTrader.Trading/Models/Accounts/VirtualAccount.cs ================================================ using System; using System.Linq; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using Newtonsoft.Json; using IntelliTrader.Core; namespace IntelliTrader.Trading { internal class VirtualAccount : TradingAccountBase { public override bool IsVirtual => true; public VirtualAccount(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ISignalsService signalsService, ITradingService tradingService) : base(loggingService, notificationService, healthCheckService, signalsService, tradingService) { } public override void Refresh() { lock (SyncRoot) { // Only done once, since all the data is always up to date if (isInitialRefresh) { Load(); isInitialRefresh = false; } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed); } } public override void Save() { lock (SyncRoot) { var tradingService = Application.Resolve(); string virtualAccountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath); var data = new TradingAccountData { Balance = balance, TradingPairs = tradingPairs }; string virtualAccountJson = JsonConvert.SerializeObject(data, Formatting.Indented); var virtualAccountFile = new FileInfo(virtualAccountFilePath); virtualAccountFile.Directory.Create(); File.WriteAllText(virtualAccountFile.FullName, virtualAccountJson); } } public void Load() { lock (SyncRoot) { var tradingService = Application.Resolve(); var virtualAccountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath); if (File.Exists(virtualAccountFilePath)) { string virtualAccountJson = File.ReadAllText(virtualAccountFilePath); var virtualAccountData = JsonConvert.DeserializeObject(virtualAccountJson); balance = virtualAccountData.Balance; tradingPairs = virtualAccountData.TradingPairs; } else { balance = tradingService.Config.VirtualAccountInitialBalance; tradingPairs = new ConcurrentDictionary(); } } } public override void Dispose() { base.Dispose(); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.AccountRefreshed); } } } ================================================ FILE: IntelliTrader.Trading/Models/BuyTrailingInfo.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Trading { internal class BuyTrailingInfo : TrailingInfo { public BuyOptions BuyOptions { get; set; } public BuyTrailingStopAction TrailingStopAction { get; set; } } } ================================================ FILE: IntelliTrader.Trading/Models/Config/TradingConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Linq; namespace IntelliTrader.Trading { internal class TradingConfig : ITradingConfig { public bool Enabled { get; set; } public string Market { get; set; } public string Exchange { get; set; } public int MaxPairs { get; set; } public decimal MinCost { get; set; } public List ExcludedPairs { get; set; } public TradePriceType TradePriceType { get; set; } public bool BuyEnabled { get; set; } public OrderType BuyType { get; set; } public decimal BuyMaxCost { get; set; } public decimal BuyMultiplier { get; set; } public decimal BuyMinBalance { get; set; } public double BuySamePairTimeout { get; set; } public decimal BuyTrailing { get; set; } public decimal BuyTrailingStopMargin { get; set; } public BuyTrailingStopAction BuyTrailingStopAction { get; set; } public bool BuyDCAEnabled { get; set; } public decimal BuyDCAMultiplier { get; set; } public decimal BuyDCAMinBalance { get; set; } public double BuyDCASamePairTimeout { get; set; } public decimal BuyDCATrailing { get; set; } public decimal BuyDCATrailingStopMargin { get; set; } public BuyTrailingStopAction BuyDCATrailingStopAction { get; set; } public bool SellEnabled { get; set; } public OrderType SellType { get; set; } public decimal SellMargin { get; set; } public decimal SellTrailing { get; set; } public decimal SellTrailingStopMargin { get; set; } public SellTrailingStopAction SellTrailingStopAction { get; set; } public bool SellStopLossEnabled { get; set; } public bool SellStopLossAfterDCA { get; set; } public double SellStopLossMinAge { get; set; } public decimal SellStopLossMargin { get; set; } public decimal SellDCAMargin { get; set; } public decimal SellDCATrailing { get; set; } public decimal SellDCATrailingStopMargin { get; set; } public SellTrailingStopAction SellDCATrailingStopAction { get; set; } public bool RepeatLastDCALevel { get; set; } public List DCALevels { get; set; } public double TradingCheckInterval { get; set; } public double AccountRefreshInterval { get; set; } public decimal AccountInitialBalance { get; set; } public DateTimeOffset AccountInitialBalanceDate { get; set; } public string AccountFilePath { get; set; } public bool VirtualTrading { get; set; } public decimal VirtualTradingFees { get; set; } public decimal VirtualAccountInitialBalance { get; set; } public string VirtualAccountFilePath { get; set; } public ITradingConfig Clone() { return new TradingConfig { Enabled = Enabled, Market = Market, Exchange = Exchange, MaxPairs = MaxPairs, MinCost = MinCost, ExcludedPairs = ExcludedPairs, TradePriceType = TradePriceType, BuyEnabled = BuyEnabled, BuyType = BuyType, BuyMaxCost = BuyMaxCost, BuyMultiplier = BuyMultiplier, BuyMinBalance = BuyMinBalance, BuySamePairTimeout = BuySamePairTimeout, BuyTrailing = BuyTrailing, BuyTrailingStopMargin = BuyTrailingStopMargin, BuyTrailingStopAction = BuyTrailingStopAction, BuyDCAEnabled = BuyDCAEnabled, BuyDCAMultiplier = BuyDCAMultiplier, BuyDCAMinBalance = BuyDCAMinBalance, BuyDCASamePairTimeout = BuyDCASamePairTimeout, BuyDCATrailing = BuyDCATrailing, BuyDCATrailingStopMargin = BuyDCATrailingStopMargin, BuyDCATrailingStopAction = BuyDCATrailingStopAction, SellEnabled = SellEnabled, SellType = SellType, SellMargin = SellMargin, SellTrailing = SellTrailing, SellTrailingStopMargin = SellTrailingStopMargin, SellTrailingStopAction = SellTrailingStopAction, SellStopLossEnabled = SellStopLossEnabled, SellStopLossAfterDCA = SellStopLossAfterDCA, SellStopLossMinAge = SellStopLossMinAge, SellStopLossMargin = SellStopLossMargin, SellDCAMargin = SellDCAMargin, SellDCATrailing = SellDCATrailing, SellDCATrailingStopMargin = SellDCATrailingStopMargin, SellDCATrailingStopAction = SellDCATrailingStopAction, RepeatLastDCALevel = RepeatLastDCALevel, DCALevels = DCALevels, TradingCheckInterval = TradingCheckInterval, AccountRefreshInterval = AccountRefreshInterval, AccountInitialBalance = AccountInitialBalance, AccountInitialBalanceDate = AccountInitialBalanceDate, AccountFilePath = AccountFilePath, VirtualTrading = VirtualTrading, VirtualTradingFees = VirtualTradingFees, VirtualAccountInitialBalance = VirtualAccountInitialBalance, VirtualAccountFilePath = VirtualAccountFilePath }; } } } ================================================ FILE: IntelliTrader.Trading/Models/PairConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Trading { internal class PairConfig : IPairConfig { public IEnumerable Rules { get; set; } public int MaxPairs { get; set; } public bool BuyEnabled { get; set; } public OrderType BuyType { get; set; } public decimal BuyMaxCost { get; set; } public decimal BuyMultiplier { get; set; } public decimal BuyMinBalance { get; set; } public double BuySamePairTimeout { get; set; } public decimal BuyTrailing { get; set; } public decimal BuyTrailingStopMargin { get; set; } public BuyTrailingStopAction BuyTrailingStopAction { get; set; } public bool SellEnabled { get; set; } public OrderType SellType { get; set; } public decimal SellMargin { get; set; } public decimal SellTrailing { get; set; } public decimal SellTrailingStopMargin { get; set; } public SellTrailingStopAction SellTrailingStopAction { get; set; } public bool SellStopLossEnabled { get; set; } public bool SellStopLossAfterDCA { get; set; } public double SellStopLossMinAge { get; set; } public decimal SellStopLossMargin { get; set; } public bool SwapEnabled { get; set; } public List SwapSignalRules { get; set; } public int SwapTimeout { get; set; } public bool ArbitrageEnabled { get; set; } public List ArbitrageMarkets { get; set; } public ArbitrageType? ArbitrageType { get; set; } public decimal? ArbitrageBuyMultiplier { get; set; } public decimal? ArbitrageSellMultiplier { get; set; } public List ArbitrageSignalRules { get; set; } public decimal? CurrentDCAMargin { get; set; } public decimal? NextDCAMargin { get; set; } } } ================================================ FILE: IntelliTrader.Trading/Models/SellTrailingInfo.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Trading { internal class SellTrailingInfo : TrailingInfo { public SellOptions SellOptions { get; set; } public SellTrailingStopAction TrailingStopAction { get; set; } public decimal SellMargin { get; set; } } } ================================================ FILE: IntelliTrader.Trading/Models/TradingPair.cs ================================================ using IntelliTrader.Core; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; namespace IntelliTrader.Trading { public class TradingPair : ITradingPair { public string Pair { get; set; } [JsonIgnore] public string FormattedName => DCALevel > 0 ? $"{Pair}({DCALevel})" : Pair; public int DCALevel => (OrderDates.Count > 0 ? (OrderDates.Count - 1) : 0) + (Metadata.AdditionalDCALevels ?? 0); public List OrderIds { get; set; } public List OrderDates { get; set; } [JsonConverter(typeof(DecimalFormatJsonConverter), 8)] public decimal Amount { get; set; } [JsonConverter(typeof(DecimalFormatJsonConverter), 8)] public decimal AveragePrice { get; set; } [JsonConverter(typeof(DecimalFormatJsonConverter), 8)] public decimal Fees { get; set; } [JsonConverter(typeof(DecimalFormatJsonConverter), 8)] public decimal Cost => GetPartialCost(Amount); [JsonIgnore] public decimal? CostOverride { get; set; } [JsonIgnore] public decimal CurrentCost => CurrentPrice * Amount; [JsonIgnore] public decimal CurrentPrice { get; set; } [JsonIgnore] public decimal CurrentSpread { get; set; } [JsonIgnore] public decimal CurrentMargin => Utils.CalculatePercentage(Cost + Fees + (Metadata.AdditionalCosts ?? 0), CurrentCost); [JsonIgnore] public double CurrentAge => OrderDates != null && OrderDates.Count > 0 ? (DateTimeOffset.Now - OrderDates.Min()).TotalDays : 0; [JsonIgnore] public double LastBuyAge => OrderDates != null && OrderDates.Count > 0 ? (DateTimeOffset.Now - OrderDates.Max()).TotalDays : 0; public OrderMetadata Metadata { get; set; } = new OrderMetadata(); public decimal GetPartialCost(decimal partialAmount) { if (CostOverride != null) { return CostOverride.Value; } else { return AveragePrice * partialAmount; } } public void OverrideCost(decimal? costOverride) { CostOverride = costOverride; } public void SetCurrentValues(decimal currentPrice, decimal currentSpread) { CurrentPrice = currentPrice; CurrentSpread = currentSpread; } public void SetMetadata(OrderMetadata metadata) { this.Metadata = metadata; } } } ================================================ FILE: IntelliTrader.Trading/Models/TradingRuleModifiers.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Trading { internal class TradingRuleModifiers { public int? MaxPairs { get; set; } public bool? BuyEnabled { get; set; } public decimal? BuyMaxCost { get; set; } public decimal? BuyMultiplier { get; set; } public decimal? BuyMinBalance { get; set; } public double? BuySamePairTimeout { get; set; } public decimal? BuyTrailing { get; set; } public decimal? BuyTrailingStopMargin { get; set; } public BuyTrailingStopAction? BuyTrailingStopAction { get; set; } public bool? BuyDCAEnabled { get; set; } public decimal? BuyDCAMultiplier { get; set; } public decimal? BuyDCAMinBalance { get; set; } public double? BuyDCASamePairTimeout { get; set; } public decimal? BuyDCATrailing { get; set; } public decimal? BuyDCATrailingStopMargin { get; set; } public BuyTrailingStopAction? BuyDCATrailingStopAction { get; set; } public bool? SellEnabled { get; set; } public decimal? SellMargin { get; set; } public decimal? SellTrailing { get; set; } public decimal? SellTrailingStopMargin { get; set; } public SellTrailingStopAction? SellTrailingStopAction { get; set; } public bool? SellStopLossEnabled { get; set; } public bool? SellStopLossAfterDCA { get; set; } public double? SellStopLossMinAge { get; set; } public decimal? SellStopLossMargin { get; set; } public decimal? SellDCAMargin { get; set; } public decimal? SellDCATrailing { get; set; } public decimal? SellDCATrailingStopMargin { get; set; } public SellTrailingStopAction? SellDCATrailingStopAction { get; set; } public bool? RepeatLastDCALevel { get; set; } public List DCALevels { get; set; } public bool? SwapEnabled { get; set; } public List SwapSignalRules { get; set; } public int? SwapTimeout { get; set; } public bool? ArbitrageEnabled { get; set; } public List ArbitrageMarkets { get; set; } public ArbitrageType? ArbitrageType { get; set; } public decimal? ArbitrageBuyMultiplier { get; set; } public decimal? ArbitrageSellMultiplier { get; set; } public List ArbitrageSignalRules { get; set; } } } ================================================ FILE: IntelliTrader.Trading/Models/TradingRulesConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Trading { internal class TradingRulesConfig { public RuleProcessingMode ProcessingMode { get; set; } public double CheckInterval { get; set; } } } ================================================ FILE: IntelliTrader.Trading/Models/TrailingInfo.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Trading { internal abstract class TrailingInfo { public decimal Trailing { get; set; } public decimal TrailingStopMargin { get; set; } public decimal InitialPrice { get; set; } public decimal LastTrailingMargin { get; set; } public decimal BestTrailingMargin { get; set; } } } ================================================ FILE: IntelliTrader.Trading/Services/OrderingService.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Exchange.Base; using System; using System.Linq; namespace IntelliTrader.Trading { internal class OrderingService : IOrderingService { private readonly ILoggingService loggingService; private readonly INotificationService notificationService; private readonly ITradingService tradingService; public OrderingService(ILoggingService loggingService, INotificationService notificationService, ITradingService tradingService) { this.loggingService = loggingService; this.notificationService = notificationService; this.tradingService = tradingService; } public IOrderDetails PlaceBuyOrder(BuyOptions options) { OrderDetails orderDetails = new OrderDetails(); tradingService.StopTrailingBuy(options.Pair); tradingService.StopTrailingSell(options.Pair); try { ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair, includeDust: true); options.Price = tradingService.GetPrice(options.Pair, TradePriceType.Ask, normalize: false); options.Amount = options.Amount ?? (options.MaxCost.Value / (options.Pair.EndsWith(Constants.Markets.USDT) ? 1 : options.Price)); options.Price = tradingService.Exchange.ClampOrderPrice(options.Pair, options.Price.Value); options.Amount = tradingService.Exchange.ClampOrderAmount(options.Pair, options.Amount.Value); if (tradingService.CanBuy(options, out string message)) { IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair); BuyOrder buyOrder = new BuyOrder { Type = pairConfig.BuyType, Date = DateTimeOffset.Now, Pair = options.Pair, Price = options.Price.Value, Amount = options.Amount.Value }; lock (tradingService.Account.SyncRoot) { loggingService.Info($"Place buy order for {tradingPair?.FormattedName ?? options.Pair}. " + $"Price: {buyOrder.Price:0.00000000}, Amount: {buyOrder.Amount:0.########}, Signal Rule: " + (options.Metadata.SignalRule ?? "N/A")); if (!tradingService.Config.VirtualTrading) { orderDetails = tradingService.Exchange.PlaceOrder(buyOrder) as OrderDetails; } else { string pairMarket = tradingService.Exchange.GetPairMarket(options.Pair); orderDetails = new OrderDetails { OrderId = DateTime.Now.ToFileTimeUtc().ToString(), Side = OrderSide.Buy, Result = OrderResult.Filled, Date = buyOrder.Date, Pair = buyOrder.Pair, Amount = buyOrder.Amount, AmountFilled = buyOrder.Amount, Price = buyOrder.Price, AveragePrice = buyOrder.Price, Fees = buyOrder.Amount * buyOrder.Price * tradingService.Config.VirtualTradingFees, FeesCurrency = pairMarket }; } NormalizeOrder(orderDetails, TradePriceType.Ask); options.Metadata.TradingRules = pairConfig.Rules.ToList(); options.Metadata.LastBuyMargin = options.Metadata.LastBuyMargin ?? tradingPair?.CurrentMargin ?? null; orderDetails.Metadata = options.Metadata; tradingService.Account.AddBuyOrder(orderDetails); tradingService.Account.Save(); tradingService.LogOrder(orderDetails); decimal fees = tradingService.CalculateOrderFees(orderDetails); tradingPair = tradingService.Account.GetTradingPair(orderDetails.Pair, includeDust: true); loggingService.Info("{@Trade}", orderDetails); loggingService.Info($"Buy order result for {orderDetails.OriginalPair ?? tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). " + $"Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, " + $"Filled: {orderDetails.AmountFilled:0.########}, Cost: {orderDetails.Cost:0.00000000}, Fees: {fees:0.00000000}"); notificationService.Notify($"Bought {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, " + $"Price: {orderDetails.AveragePrice:0.00000000}, Cost: {(orderDetails.Cost + fees):0.00000000}"); } tradingService.ReapplyTradingRules(); } else { loggingService.Info(message); } } catch (Exception ex) { loggingService.Error($"Unable to place buy order for {options.Pair}", ex); notificationService.Notify($"Unable to buy {options.Pair}: {ex.Message}"); } return orderDetails; } public IOrderDetails PlaceSellOrder(SellOptions options) { OrderDetails orderDetails = new OrderDetails(); tradingService.StopTrailingSell(options.Pair); tradingService.StopTrailingBuy(options.Pair); try { string normalizedPair = tradingService.NormalizePair(options.Pair); ITradingPair tradingPair = tradingService.Account.GetTradingPair(normalizedPair, includeDust: true); options.Price = tradingService.GetPrice(options.Pair, TradePriceType.Bid); options.Amount = options.Amount ?? tradingPair?.Amount ?? 0; options.Price = options.Price != 1 ? tradingService.Exchange.ClampOrderPrice(options.Pair, options.Price.Value) : 1; // 1 = USDT price options.Amount = tradingService.Exchange.ClampOrderAmount(options.Pair, options.Amount.Value); if (tradingService.CanSell(options, out string message)) { IPairConfig pairConfig = tradingService.GetPairConfig(normalizedPair); SellOrder sellOrder = new SellOrder { Type = pairConfig.SellType, Date = DateTimeOffset.Now, Pair = options.Pair, Price = options.Price.Value, Amount = options.Amount.Value }; lock (tradingService.Account.SyncRoot) { tradingPair.SetCurrentValues(tradingService.GetPrice(normalizedPair), tradingService.Exchange.GetPriceSpread(normalizedPair)); string sellPairName = normalizedPair != options.Pair ? options.Pair : tradingPair.FormattedName; loggingService.Info($"Place sell order for {sellPairName}. " + $"Price: {sellOrder.Price:0.00000000}, Amount: {sellOrder.Amount:0.########}, Margin: {tradingPair.CurrentMargin:0.00}"); if (!tradingService.Config.VirtualTrading) { orderDetails = tradingService.Exchange.PlaceOrder(sellOrder) as OrderDetails; } else { string pairMarket = tradingService.Exchange.GetPairMarket(options.Pair); orderDetails = new OrderDetails { OrderId = DateTime.Now.ToFileTimeUtc().ToString(), Side = OrderSide.Sell, Result = OrderResult.Filled, Date = sellOrder.Date, Pair = sellOrder.Pair, Amount = sellOrder.Amount, AmountFilled = sellOrder.Amount, Price = sellOrder.Price, AveragePrice = sellOrder.Price, Fees = sellOrder.Amount * sellOrder.Price * tradingService.Config.VirtualTradingFees, FeesCurrency = pairMarket }; } NormalizeOrder(orderDetails, TradePriceType.Bid); tradingPair.SetMetadata(tradingPair.Metadata.MergeWith(options.Metadata)); orderDetails.Metadata = tradingPair.Metadata; var tradeResult = tradingService.Account.AddSellOrder(orderDetails) as TradeResult; tradeResult.IsSwap = options.Swap; tradeResult.IsArbitrage = options.Arbitrage; tradingService.Account.Save(); tradingService.LogOrder(orderDetails); decimal fees = tradingService.CalculateOrderFees(orderDetails); decimal margin = (tradeResult.Profit / (tradeResult.Cost + (tradeResult.Metadata.AdditionalCosts ?? 0)) * 100); string swapPair = options.Metadata.SwapPair != null ? $", Swap Pair: {options.Metadata.SwapPair}" : ""; string arbitrage = options.Metadata.Arbitrage != null ? $", Arbitrage: {options.Metadata.Arbitrage} ({options.Metadata.ArbitragePercentage:0.00})" : ""; loggingService.Info("{@Trade}", orderDetails); loggingService.Info("{@Trade}", tradeResult); loggingService.Info($"Sell order result for {orderDetails.OriginalPair ?? tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). " + $"Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, Filled: {orderDetails.AmountFilled:0.########}, " + $"Cost: {orderDetails.Cost:0.00000000}, Fees: {fees:0.00000000}, Margin: {margin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}{arbitrage}"); notificationService.Notify($"Sold {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, " + $"Price: {orderDetails.AveragePrice:0.00000000}, Margin: {margin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}{arbitrage}"); } tradingService.ReapplyTradingRules(); } else { loggingService.Info(message); } } catch (Exception ex) { loggingService.Error($"Unable to place sell order for {options.Pair}", ex); notificationService.Notify($"Unable to sell {options.Pair}: {ex.Message}"); } return orderDetails; } private void NormalizeOrder(OrderDetails orderDetails, TradePriceType priceType) { if (!tradingService.IsNormalizedPair(orderDetails.Pair)) { string pairMarket = tradingService.Exchange.GetPairMarket(orderDetails.Pair); if (pairMarket != Constants.Markets.USDT || orderDetails.Price != 1) { orderDetails.Price = tradingService.Exchange.ConvertPrice(orderDetails.Pair, orderDetails.Price, tradingService.Config.Market, priceType); orderDetails.AveragePrice = tradingService.Exchange.ConvertPrice(orderDetails.Pair, orderDetails.AveragePrice, tradingService.Config.Market, priceType); } else if (pairMarket == Constants.Markets.USDT && orderDetails.Pair.StartsWith(tradingService.Config.Market)) { orderDetails.Amount = orderDetails.Amount / tradingService.GetPrice(orderDetails.Pair, priceType, normalize: false); orderDetails.AmountFilled = orderDetails.AmountFilled / tradingService.GetPrice(orderDetails.Pair, priceType, normalize: false); } if (orderDetails.FeesCurrency == tradingService.Exchange.GetPairMarket(orderDetails.Pair)) { orderDetails.Fees = tradingService.Exchange.ConvertPrice(orderDetails.Pair, orderDetails.Fees, tradingService.Config.Market, priceType); orderDetails.FeesCurrency = tradingService.Config.Market; } orderDetails.OriginalPair = orderDetails.Pair; if (!orderDetails.Pair.StartsWith(tradingService.Config.Market)) { orderDetails.Pair = tradingService.Exchange.ChangeMarket(orderDetails.Pair, tradingService.Config.Market); } orderDetails.IsNormalized = true; } } } } ================================================ FILE: IntelliTrader.Trading/Services/TradingService.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Exchange.Base; using IntelliTrader.Signals.Base; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; namespace IntelliTrader.Trading { internal class TradingService : ConfigrableServiceBase, ITradingService { private const int MIN_INTERVAL_BETWEEN_BUY_AND_SELL = 7000; private const decimal DEFAULT_ARBITRAGE_BUY_MULTIPLIER = 0.99M; private const decimal DEFAULT_ARBITRAGE_SELL_MULTIPLIER = 0.99M; public override string ServiceName => Constants.ServiceNames.TradingService; ITradingConfig ITradingService.Config => Config; public IModuleRules Rules { get; private set; } public TradingRulesConfig RulesConfig { get; private set; } public IExchangeService Exchange { get; private set; } public ITradingAccount Account { get; private set; } public ConcurrentStack OrderHistory { get; private set; } = new ConcurrentStack(); public bool IsTradingSuspended { get; private set; } private readonly ILoggingService loggingService; private readonly INotificationService notificationService; private readonly IHealthCheckService healthCheckService; private readonly ITasksService tasksService; private IOrderingService orderingService; private IRulesService rulesService; private ISignalsService signalsService; private TradingTimedTask tradingTimedTask; private TradingRulesTimedTask tradingRulesTimedTask; private AccountRefreshTimedTask accountRefreshTimedTask; private bool tradingForcefullySuspended; private object syncRoot = new object(); public TradingService(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ITasksService tasksService) { this.loggingService = loggingService; this.notificationService = notificationService; this.healthCheckService = healthCheckService; this.tasksService = tasksService; var isBacktesting = Application.Resolve().Config.Enabled && Application.Resolve().Config.Replay; if (isBacktesting) { this.Exchange = Application.ResolveOptionalNamed(Constants.ServiceNames.BacktestingExchangeService); } else { this.Exchange = Application.ResolveOptionalNamed(Config.Exchange); } if (this.Exchange == null) { throw new Exception($"Unsupported exchange: {Config.Exchange}"); } } public void Start() { loggingService.Info($"Start Trading service (Virtual: {Config.VirtualTrading})..."); IsTradingSuspended = true; orderingService = Application.Resolve(); rulesService = Application.Resolve(); OnTradingRulesChanged(); rulesService.RegisterRulesChangeCallback(OnTradingRulesChanged); Exchange.Start(Config.VirtualTrading); signalsService = Application.Resolve(); if (!Config.VirtualTrading) { Account = new ExchangeAccount(loggingService, notificationService, healthCheckService, signalsService, this); } else { Account = new VirtualAccount(loggingService, notificationService, healthCheckService, signalsService, this); } accountRefreshTimedTask = tasksService.AddTask( name: nameof(AccountRefreshTimedTask), task: new AccountRefreshTimedTask(loggingService, healthCheckService, this), interval: Config.AccountRefreshInterval * 1000 / Application.Speed, startDelay: Constants.TaskDelays.ZeroDelay, startTask: false, runNow: true, skipIteration: 0); if (signalsService.Config.Enabled) { signalsService.Start(); } tradingTimedTask = tasksService.AddTask( name: nameof(TradingTimedTask), task: new TradingTimedTask(loggingService, notificationService, healthCheckService, signalsService, orderingService, this), interval: Config.TradingCheckInterval * 1000 / Application.Speed, startDelay: Constants.TaskDelays.NormalDelay, startTask: false, runNow: false, skipIteration: 0); tradingRulesTimedTask = tasksService.AddTask( name: nameof(TradingRulesTimedTask), task: new TradingRulesTimedTask(loggingService, notificationService, healthCheckService, rulesService, signalsService, this), interval: RulesConfig.CheckInterval * 1000 / Application.Speed, startDelay: Constants.TaskDelays.MidDelay, startTask: false, runNow: false, skipIteration: 0); IsTradingSuspended = false; loggingService.Info("Trading service started"); } public void Stop() { loggingService.Info("Stop Trading service..."); Exchange.Stop(); if (signalsService.Config.Enabled) { signalsService.Stop(); } tasksService.RemoveTask(nameof(TradingTimedTask), stopTask: true); tasksService.RemoveTask(nameof(TradingRulesTimedTask), stopTask: true); tasksService.RemoveTask(nameof(AccountRefreshTimedTask), stopTask: true); Account.Dispose(); rulesService.UnregisterRulesChangeCallback(OnTradingRulesChanged); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingRulesProcessed); healthCheckService.RemoveHealthCheck(Constants.HealthChecks.TradingPairsProcessed); loggingService.Info("Trading service stopped"); } public void ResumeTrading(bool forced) { if (IsTradingSuspended && (!tradingForcefullySuspended || forced)) { loggingService.Info("Trading started"); IsTradingSuspended = false; tradingTimedTask.Start(); tradingRulesTimedTask.Start(); tradingRulesTimedTask.RunNow(); } } public void SuspendTrading(bool forced) { if (!IsTradingSuspended) { loggingService.Info("Trading suspended"); IsTradingSuspended = true; tradingForcefullySuspended = forced; tradingRulesTimedTask.Stop(); tradingTimedTask.Stop(); tradingTimedTask.StopTrailing(); } } public IPairConfig GetPairConfig(string pair) { return tradingRulesTimedTask.GetPairConfig(pair); } public void ReapplyTradingRules() { tradingRulesTimedTask.RunNow(); } public void Buy(BuyOptions options) { lock (syncRoot) { PauseTasks(); try { IRule rule = signalsService.Rules.Entries.FirstOrDefault(r => r.Name == options.Metadata.SignalRule); RuleAction ruleAction = rule?.Action ?? RuleAction.Default; IPairConfig pairConfig = GetPairConfig(options.Pair); bool arbitragePair = pairConfig.ArbitrageEnabled && pairConfig.ArbitrageSignalRules.Contains(options.Metadata.SignalRule); if (arbitragePair) { Arbitrage arbitrage = Exchange.GetArbitrage(options.Pair, Config.Market, pairConfig.ArbitrageMarkets, pairConfig.ArbitrageType); if (arbitrage.IsAssigned) { Arbitrage(new ArbitrageOptions(options.Pair, arbitrage, options.Metadata)); } } else { ITradingPair swappedPair = Account.GetTradingPairs().OrderBy(p => p.CurrentMargin).FirstOrDefault(tradingPair => { IPairConfig tradingPairConfig = GetPairConfig(tradingPair.Pair); return tradingPairConfig.SellEnabled && tradingPairConfig.SwapEnabled && tradingPairConfig.SwapSignalRules != null && tradingPairConfig.SwapSignalRules.Contains(options.Metadata.SignalRule) && tradingPairConfig.SwapTimeout < (DateTimeOffset.Now - tradingPair.OrderDates.DefaultIfEmpty().Max()).TotalSeconds; }); if (swappedPair != null) { Swap(new SwapOptions(swappedPair.Pair, options.Pair, options.Metadata)); } else if (ruleAction == RuleAction.Default) { if (CanBuy(options, out string message)) { tradingTimedTask.InitiateBuy(options); } else { loggingService.Debug(message); } } } } finally { ContinueTasks(); } } } public void Sell(SellOptions options) { lock (syncRoot) { PauseTasks(); try { if (CanSell(options, out string message)) { tradingTimedTask.InitiateSell(options); } else { loggingService.Debug(message); } } finally { ContinueTasks(); } } } public void Swap(SwapOptions options) { lock (syncRoot) { PauseTasks(); try { if (CanSwap(options, out string message)) { ITradingPair oldTradingPair = Account.GetTradingPair(options.OldPair); var sellOptions = new SellOptions(options.OldPair) { Swap = true, ManualOrder = options.ManualOrder, Metadata = new OrderMetadata { SwapPair = options.NewPair } }; if (CanSell(sellOptions, out message)) { decimal currentMargin = oldTradingPair.CurrentMargin; decimal additionalCosts = oldTradingPair.Cost - oldTradingPair.CurrentCost + (oldTradingPair.Metadata.AdditionalCosts ?? 0); int additionalDCALevels = oldTradingPair.DCALevel; IOrderDetails sellOrderDetails = orderingService.PlaceSellOrder(sellOptions); if (!Account.HasTradingPair(options.OldPair)) { var buyOptions = new BuyOptions(options.NewPair) { Swap = true, ManualOrder = options.ManualOrder, MaxCost = sellOrderDetails.Cost, Metadata = options.Metadata }; buyOptions.Metadata.LastBuyMargin = currentMargin; buyOptions.Metadata.SwapPair = options.OldPair; buyOptions.Metadata.AdditionalDCALevels = additionalDCALevels; buyOptions.Metadata.AdditionalCosts = additionalCosts; IOrderDetails buyOrderDetails = orderingService.PlaceBuyOrder(buyOptions); var newTradingPair = Account.GetTradingPair(options.NewPair) as TradingPair; if (newTradingPair != null) { newTradingPair.Metadata.AdditionalCosts += CalculateOrderFees(sellOrderDetails); loggingService.Info($"Swap {oldTradingPair.FormattedName} for {newTradingPair.FormattedName}. " + $"Old margin: {oldTradingPair.CurrentMargin:0.00}, new margin: {newTradingPair.CurrentMargin:0.00}"); } else { loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to buy {options.NewPair}"); notificationService.Notify($"Unable to swap {options.OldPair} for {options.NewPair}: Failed to buy {options.NewPair}"); } } else { loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to sell {options.OldPair}"); } } else { loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}: {message}"); } } else { loggingService.Info(message); } } finally { ContinueTasks(); } } } public void Arbitrage(ArbitrageOptions options) { lock (syncRoot) { PauseTasks(); try { if (CanArbitrage(options, out string message)) { if (CanBuy(new BuyOptions(options.Pair) { Amount = 1 }, out message)) { options.Metadata.Arbitrage = $"{options.Arbitrage.Market}-" + options.Arbitrage.Type ?? "All"; options.Metadata.ArbitragePercentage = options.Arbitrage.Percentage; loggingService.Info($"{options.Arbitrage.Type} arbitrage {options.Pair} on {options.Arbitrage.Market}. Percentage: {options.Arbitrage.Percentage:0.00}"); if (options.Arbitrage.Type == ArbitrageType.Direct) { ArbitrageDirect(options); } else if (options.Arbitrage.Type == ArbitrageType.Reverse) { ArbitrageReverse(options); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}: {message}"); } } else { loggingService.Info(message); } } finally { ContinueTasks(); } } } private void ArbitrageDirect(ArbitrageOptions options) { string arbitragePair = options.Pair; ITradingPair existingArbitragePair = Account.GetTradingPair(arbitragePair); IPairConfig pairConfig = GetPairConfig(options.Pair); bool useExistingArbitragePair = (existingArbitragePair != null && existingArbitragePair.CurrentCost > pairConfig.BuyMaxCost && existingArbitragePair.AveragePrice <= existingArbitragePair.CurrentPrice); var buyArbitragePairOptions = new BuyOptions(arbitragePair) { Arbitrage = true, MaxCost = pairConfig.BuyMaxCost, ManualOrder = options.ManualOrder, IgnoreBalance = useExistingArbitragePair, Metadata = options.Metadata }; if (CanBuy(buyArbitragePairOptions, out string message)) { IOrderDetails buyArbitragePairOrderDetails = null; if (useExistingArbitragePair) { buyArbitragePairOrderDetails = Account.AddBlankOrder(buyArbitragePairOptions.Pair, buyArbitragePairOptions.MaxCost.Value / GetPrice(buyArbitragePairOptions.Pair, TradePriceType.Ask), includeFees: false); loggingService.Info($"Use existing arbitrage pair for arbitrage: {arbitragePair}. " + $"Average price: {existingArbitragePair.AveragePrice}, Current price: {existingArbitragePair.CurrentPrice}"); } else { buyArbitragePairOrderDetails = orderingService.PlaceBuyOrder(buyArbitragePairOptions); } if (buyArbitragePairOrderDetails.Result == OrderResult.Filled) { decimal buyArbitragePairFees = CalculateOrderFees(buyArbitragePairOrderDetails); string flippedArbitragePair = Exchange.ChangeMarket(arbitragePair, options.Arbitrage.Market.ToString()); var sellArbitragePairOptions = new SellOptions(flippedArbitragePair) { Arbitrage = true, Amount = buyArbitragePairOrderDetails.AmountFilled, ManualOrder = options.ManualOrder, Metadata = options.Metadata.MergeWith(new OrderMetadata { IsTransitional = true }) }; IOrderDetails sellArbitragePairOrderDetails = orderingService.PlaceSellOrder(sellArbitragePairOptions); if (sellArbitragePairOrderDetails.Result == OrderResult.Filled) { decimal sellArbitragePairMultiplier = pairConfig.ArbitrageSellMultiplier ?? DEFAULT_ARBITRAGE_SELL_MULTIPLIER; decimal sellArbitragePairFees = CalculateOrderFees(sellArbitragePairOrderDetails); options.Metadata.FeesNonDeductible = buyArbitragePairFees * sellArbitragePairMultiplier; decimal sellMarketPairAmount = sellArbitragePairOrderDetails.AmountFilled * GetPrice(flippedArbitragePair, TradePriceType.Bid, normalize: false) * sellArbitragePairMultiplier; string marketPair = Exchange.GetArbitrageMarketPair(options.Arbitrage.Market); var sellMarketPairOptions = new SellOptions(marketPair) { Arbitrage = true, Amount = sellMarketPairAmount, ManualOrder = options.ManualOrder, Metadata = options.Metadata.MergeWith(new OrderMetadata { IsTransitional = false, OriginalPair = arbitragePair }) }; existingArbitragePair = Account.GetTradingPair(marketPair); existingArbitragePair.OverrideCost((buyArbitragePairOrderDetails.Cost + sellArbitragePairFees * 2) * sellArbitragePairMultiplier); IOrderDetails sellMarketPairOrderDetails = orderingService.PlaceSellOrder(sellMarketPairOptions); existingArbitragePair.OverrideCost(null); if (sellMarketPairOrderDetails.Result == OrderResult.Filled) { loggingService.Info($"{pairConfig.ArbitrageType} arbitrage successful: {arbitragePair} -> {flippedArbitragePair} -> {marketPair}"); } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to sell market pair {arbitragePair}"); notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to sell market pair {arbitragePair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to sell arbitrage pair {flippedArbitragePair}"); notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to sell arbitrage pair {flippedArbitragePair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy arbitrage pair {arbitragePair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}: {message}"); } } private void ArbitrageReverse(ArbitrageOptions options) { string marketPair = Exchange.GetArbitrageMarketPair(options.Arbitrage.Market); ITradingPair existingMarketPair = Account.GetTradingPair(marketPair); IPairConfig pairConfig = GetPairConfig(options.Pair); bool useExistingMarketPair = (existingMarketPair != null && existingMarketPair.CurrentCost > pairConfig.BuyMaxCost && existingMarketPair.AveragePrice <= existingMarketPair.CurrentPrice); var buyMarketPairOptions = new BuyOptions(marketPair) { Arbitrage = true, MaxCost = pairConfig.BuyMaxCost, ManualOrder = options.ManualOrder, IgnoreBalance = useExistingMarketPair, Metadata = options.Metadata }; if (CanBuy(buyMarketPairOptions, out string message)) { IOrderDetails buyMarketPairOrderDetails = null; if (useExistingMarketPair) { buyMarketPairOrderDetails = Account.AddBlankOrder(buyMarketPairOptions.Pair, buyMarketPairOptions.MaxCost.Value / GetPrice(buyMarketPairOptions.Pair, TradePriceType.Ask), includeFees: false); loggingService.Info($"Use existing market pair for arbitrage: {marketPair}. " + $"Average price: {existingMarketPair.AveragePrice}, Current price: {existingMarketPair.CurrentPrice}"); } else { buyMarketPairOrderDetails = orderingService.PlaceBuyOrder(buyMarketPairOptions); } if (buyMarketPairOrderDetails.Result == OrderResult.Filled) { decimal buyArbitragePairMultiplier = pairConfig.ArbitrageBuyMultiplier ?? DEFAULT_ARBITRAGE_BUY_MULTIPLIER; decimal buyMarketPairFees = CalculateOrderFees(buyMarketPairOrderDetails); string arbitragePair = Exchange.ChangeMarket(options.Pair, options.Arbitrage.Market.ToString()); decimal buyArbitragePairAmount = options.Arbitrage.Market == ArbitrageMarket.USDT ? buyMarketPairOrderDetails.AmountFilled * GetPrice(buyMarketPairOrderDetails.Pair, TradePriceType.Ask, normalize: false) / GetPrice(arbitragePair, TradePriceType.Ask) : buyMarketPairOrderDetails.AmountFilled / GetPrice(arbitragePair, TradePriceType.Ask); var buyArbitragePairOptions = new BuyOptions(arbitragePair) { Arbitrage = true, ManualOrder = options.ManualOrder, Amount = buyArbitragePairAmount * buyArbitragePairMultiplier, Metadata = options.Metadata }; IOrderDetails buyArbitragePairOrderDetails = orderingService.PlaceBuyOrder(buyArbitragePairOptions); if (buyArbitragePairOrderDetails.Result == OrderResult.Filled) { decimal buyArbitragePairFees = CalculateOrderFees(buyArbitragePairOrderDetails); options.Metadata.FeesNonDeductible = buyMarketPairFees * buyArbitragePairMultiplier; var sellArbitragePairOptions = new SellOptions(buyArbitragePairOrderDetails.Pair) { Arbitrage = true, Amount = buyArbitragePairOrderDetails.AmountFilled, ManualOrder = options.ManualOrder, Metadata = options.Metadata }; TradingPair existingArbitragePair = Account.GetTradingPair(buyArbitragePairOrderDetails.Pair) as TradingPair; existingArbitragePair.OverrideCost(buyArbitragePairOrderDetails.Cost + buyArbitragePairFees * 2); IOrderDetails sellArbitragePairOrderDetails = orderingService.PlaceSellOrder(sellArbitragePairOptions); existingArbitragePair.OverrideCost(null); if (sellArbitragePairOrderDetails.Result == OrderResult.Filled) { loggingService.Info($"{pairConfig.ArbitrageType} arbitrage successful: {marketPair} -> {arbitragePair} -> {existingArbitragePair.Pair}"); } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to sell arbitrage pair {arbitragePair}"); notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to sell arbitrage pair {arbitragePair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy arbitrage pair {arbitragePair}"); notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to buy arbitrage pair {arbitragePair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy market pair {marketPair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}: {message}"); } } public bool CanBuy(BuyOptions options, out string message) { IPairConfig pairConfig = GetPairConfig(options.Pair); if (!options.ManualOrder && !options.Swap && IsTradingSuspended) { message = $"Cancel buy request for {options.Pair}. Reason: trading suspended"; return false; } else if (!options.ManualOrder && !options.Swap && !pairConfig.BuyEnabled) { message = $"Cancel buy request for {options.Pair}. Reason: buying not enabled"; return false; } else if (!options.ManualOrder && Config.ExcludedPairs.Contains(options.Pair)) { message = $"Cancel buy request for {options.Pair}. Reason: exluded pair"; return false; } else if (!options.ManualOrder && !options.Arbitrage && !options.IgnoreExisting && Account.HasTradingPair(options.Pair)) { message = $"Cancel buy request for {options.Pair}. Reason: pair already exists"; return false; } else if (!options.ManualOrder && !options.Swap && !options.Arbitrage && pairConfig.MaxPairs != 0 && Account.GetTradingPairs().Count() >= pairConfig.MaxPairs && !Account.HasTradingPair(options.Pair)) { message = $"Cancel buy request for {options.Pair}. Reason: maximum pairs reached"; return false; } else if (!options.ManualOrder && !options.Swap && !options.IgnoreBalance && pairConfig.BuyMinBalance != 0 && (Account.GetBalance() - options.MaxCost) < pairConfig.BuyMinBalance && Exchange.GetPairMarket(options.Pair) == Config.Market) { message = $"Cancel buy request for {options.Pair}. Reason: minimum balance reached"; return false; } else if (options.Price != null && options.Price <= 0) { message = $"Cancel buy request for {options.Pair}. Reason: invalid price"; return false; } else if (options.Amount != null && options.Amount <= 0) { message = $"Cancel buy request for {options.Pair}. Reason: invalid amount"; return false; } else if (!options.IgnoreBalance && Account.GetBalance() < options.MaxCost && Exchange.GetPairMarket(options.Pair) == Config.Market) { message = $"Cancel buy request for {options.Pair}. Reason: not enough balance"; return false; } else if (options.Amount == null && options.MaxCost == null || options.Amount != null && options.MaxCost != null) { message = $"Cancel buy request for {options.Pair}. Reason: either max cost or amount needs to be specified (not both)"; } else if (!options.ManualOrder && !options.Swap && !options.Arbitrage && pairConfig.BuySamePairTimeout > 0 && OrderHistory.Any(h => h.Side == OrderSide.Buy && (h.Pair == options.Pair || h.Pair == h.OriginalPair)) && (DateTimeOffset.Now - OrderHistory.Where(h => (h.Pair == options.Pair || h.Pair == h.OriginalPair)).Max(h => h.Date)).TotalSeconds < pairConfig.BuySamePairTimeout) { var elapsedSeconds = (DateTimeOffset.Now - OrderHistory.Where(h => (h.Pair == options.Pair || h.Pair == h.OriginalPair)).Max(h => h.Date)).TotalSeconds; message = $"Cancel buy request for {options.Pair}. Reason: buy same pair timeout (elapsed: {elapsedSeconds:0.#}, timeout: {pairConfig.BuySamePairTimeout:0.#})"; return false; } message = null; return true; } public bool CanSell(SellOptions options, out string message) { IPairConfig pairConfig = GetPairConfig(options.Pair); if (!options.ManualOrder && !options.Arbitrage && IsTradingSuspended) { message = $"Cancel sell request for {options.Pair}. Reason: trading suspended"; return false; } else if (!options.ManualOrder && !options.Arbitrage && !pairConfig.SellEnabled) { message = $"Cancel sell request for {options.Pair}. Reason: selling not enabled"; return false; } else if (!options.ManualOrder && !options.Arbitrage && Config.ExcludedPairs.Contains(options.Pair)) { message = $"Cancel sell request for {options.Pair}. Reason: excluded pair"; return false; } else if (!Account.HasTradingPair(options.Pair, includeDust: true) && !Account.HasTradingPair(NormalizePair(options.Pair), includeDust: true)) { message = $"Cancel sell request for {options.Pair}. Reason: pair does not exist"; return false; } else if (options.Price != null && options.Price <= 0) { message = $"Cancel sell request for {options.Pair}. Reason: invalid price"; return false; } else if (options.Amount != null && options.Amount <= 0) { message = $"Cancel sell request for {options.Pair}. Reason: invalid amount"; return false; } else if (options.Amount != null && options.Price != null && (options.Amount * options.Price) < Config.MinCost) { message = $"Cancel sell request for {options.Pair}. Reason: dust"; return false; } else if (!options.ManualOrder && !options.Arbitrage && (DateTimeOffset.Now - Account.GetTradingPair(options.Pair, includeDust: true).OrderDates.DefaultIfEmpty().Max()). TotalMilliseconds < (MIN_INTERVAL_BETWEEN_BUY_AND_SELL / Application.Speed)) { message = $"Cancel sell request for {options.Pair}. Reason: pair just bought"; return false; } message = null; return true; } public bool CanSwap(SwapOptions options, out string message) { if (!Account.HasTradingPair(options.OldPair)) { message = $"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: pair does not exist"; return false; } else if (Account.HasTradingPair(options.NewPair)) { message = $"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: pair already exists"; return false; } else if (!options.ManualOrder && !GetPairConfig(options.OldPair).SellEnabled) { message = $"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: selling not enabled"; return false; } else if (!options.ManualOrder && !GetPairConfig(options.NewPair).BuyEnabled) { message = $"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: buying not enabled"; return false; } else if (Account.GetBalance() < Account.GetTradingPair(options.OldPair).CurrentCost * 0.01M) { message = $"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: not enough balance"; return false; } else if (!Exchange.GetMarketPairs(Config.Market).Contains(options.NewPair)) { message = $"Cancel swap request {options.OldPair} for {options.NewPair}. Reason: {options.NewPair} is not a valid pair"; return false; } message = null; return true; } public bool CanArbitrage(ArbitrageOptions options, out string message) { if (Account.HasTradingPair(options.Pair)) { message = $"Cancel arbitrage request {options.Pair}. Reason: pair already exist"; return false; } else if (!options.ManualOrder && !GetPairConfig(options.Pair).BuyEnabled) { message = $"Cancel arbitrage request for {options.Pair}. Reason: buying not enabled"; return false; } else if (!Exchange.GetMarketPairs(Config.Market).Contains(options.Pair)) { message = $"Cancel arbitrage request for {options.Pair}. Reason: {options.Pair} is not a valid pair"; return false; } message = null; return true; } public decimal GetPrice(string pair, TradePriceType? priceType = null, bool normalize = true) { if (normalize) { if (pair == Config.Market + Constants.Markets.USDT) { return 1; } } return Exchange.GetPrice(pair, priceType ?? Config.TradePriceType); } public decimal CalculateOrderFees(IOrderDetails order) { decimal orderFees = 0; if (order.Fees != 0 && order.FeesCurrency != null) { if (order.FeesCurrency == Config.Market) { orderFees = order.Fees; } else { string feesPair = order.FeesCurrency + Config.Market; orderFees = GetPrice(feesPair, TradePriceType.Ask) * order.Fees; } } return orderFees; } public bool IsNormalizedPair(string pair) { return Exchange.GetPairMarket(pair) == Config.Market; } public string NormalizePair(string pair) { return Exchange.ChangeMarket(pair, Config.Market); } public void LogOrder(IOrderDetails order) { OrderHistory.Push(order); } public List GetTrailingBuys() { return tradingTimedTask.GetTrailingBuys(); } public List GetTrailingSells() { return tradingTimedTask.GetTrailingSells(); } public void StopTrailingBuy(string pair) { tradingTimedTask.StopTrailingBuy(pair); } public void StopTrailingSell(string pair) { tradingTimedTask.StopTrailingSell(pair); } private void OnTradingRulesChanged() { Rules = rulesService.GetRules(ServiceName); RulesConfig = Rules.GetConfiguration(); } protected override void PrepareConfig() { if (Config.ExcludedPairs == null) { Config.ExcludedPairs = new List(); } if (Config.DCALevels == null) { Config.DCALevels = new List(); } } private void PauseTasks() { tasksService.GetTask(nameof(TradingTimedTask)).Pause(); tasksService.GetTask(nameof(TradingRulesTimedTask)).Pause(); tasksService.GetTask(nameof(SignalRulesTimedTask)).Pause(); tasksService.GetTask("BacktestingLoadSnapshotsTimedTask")?.Pause(); } private void ContinueTasks() { tasksService.GetTask(nameof(TradingTimedTask)).Continue(); tasksService.GetTask(nameof(TradingRulesTimedTask)).Continue(); tasksService.GetTask(nameof(SignalRulesTimedTask)).Continue(); tasksService.GetTask("BacktestingLoadSnapshotsTimedTask")?.Continue(); } } } ================================================ FILE: IntelliTrader.Trading/TimedTasks/AccountRefreshTimedTask.cs ================================================ using IntelliTrader.Core; namespace IntelliTrader.Trading { public class AccountRefreshTimedTask : HighResolutionTimedTask { private readonly ILoggingService loggingService; private readonly IHealthCheckService healthCheckService; private readonly ITradingService tradingService; public AccountRefreshTimedTask(ILoggingService loggingService, IHealthCheckService healthCheckService, ITradingService tradingService) { this.loggingService = loggingService; this.healthCheckService = healthCheckService; this.tradingService = tradingService; } protected override void Run() { tradingService.Account.Refresh(); } } } ================================================ FILE: IntelliTrader.Trading/TimedTasks/TradingRulesTimedTask.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; namespace IntelliTrader.Trading { public class TradingRulesTimedTask : HighResolutionTimedTask { private readonly ILoggingService loggingService; private readonly INotificationService notificationService; private readonly IHealthCheckService healthCheckService; private readonly IRulesService rulesService; private readonly ISignalsService signalsService; private readonly TradingService tradingService; private readonly ConcurrentDictionary pairConfigs = new ConcurrentDictionary(); public TradingRulesTimedTask(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, IRulesService rulesService, ISignalsService signalsService, ITradingService tradingService) { this.loggingService = loggingService; this.notificationService = notificationService; this.healthCheckService = healthCheckService; this.rulesService = rulesService; this.signalsService = signalsService; this.tradingService = tradingService as TradingService; } protected override void Run() { ProcessAllRules(); } public IPairConfig GetPairConfig(string pair) { PairConfig pairConfig; if (!pairConfigs.TryGetValue(pair, out pairConfig)) { ProcessAllRules(); if (!pairConfigs.TryGetValue(pair, out pairConfig)) { return CreatePairConfig(pair, tradingService.Config.Clone(), new PairConfig(), new List()); } } return pairConfig; } public void ProcessAllRules() { IEnumerable enabledRules = tradingService.Rules?.Entries?.Where(r => r.Enabled) ?? new List(); List allPairs = tradingService.Exchange.GetMarketPairs(tradingService.Config.Market).ToList(); double? globalRating = signalsService.GetGlobalRating(); foreach (string pair in allPairs) { IEnumerable signalsByPair = signalsService.GetSignalsByPair(pair); if (signalsByPair != null) { Dictionary signals = signalsByPair.ToDictionary(s => s.Name, s => s); ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair); TradingConfig modifiedTradingConfig = tradingService.Config.Clone() as TradingConfig; PairConfig modifiedPairConfig = new PairConfig(); pairConfigs.TryGetValue(pair, out PairConfig oldPairConfig); var appliedRules = new List(); foreach (var rule in enabledRules) { if (rulesService.CheckConditions(rule.Conditions, signals, globalRating, pair, tradingPair)) { var modifiers = rule.GetModifiers(); if (modifiers != null) { // Base Trading Config modifiedTradingConfig.MaxPairs = modifiers.MaxPairs ?? modifiedTradingConfig.MaxPairs; modifiedTradingConfig.BuyEnabled = modifiers.BuyEnabled ?? modifiedTradingConfig.BuyEnabled; modifiedTradingConfig.BuyMaxCost = modifiers.BuyMaxCost ?? modifiedTradingConfig.BuyMaxCost; modifiedTradingConfig.BuyMultiplier = modifiers.BuyMultiplier ?? modifiedTradingConfig.BuyMultiplier; modifiedTradingConfig.BuyMinBalance = modifiers.BuyMinBalance ?? modifiedTradingConfig.BuyMinBalance; modifiedTradingConfig.BuySamePairTimeout = modifiers.BuySamePairTimeout ?? modifiedTradingConfig.BuySamePairTimeout; modifiedTradingConfig.BuyTrailing = modifiers.BuyTrailing ?? modifiedTradingConfig.BuyTrailing; modifiedTradingConfig.BuyTrailingStopMargin = modifiers.BuyTrailingStopMargin ?? modifiedTradingConfig.BuyTrailingStopMargin; modifiedTradingConfig.BuyTrailingStopAction = modifiers.BuyTrailingStopAction ?? modifiedTradingConfig.BuyTrailingStopAction; modifiedTradingConfig.BuyDCAEnabled = modifiers.BuyDCAEnabled ?? modifiedTradingConfig.BuyDCAEnabled; modifiedTradingConfig.BuyDCAMultiplier = modifiers.BuyDCAMultiplier ?? modifiedTradingConfig.BuyDCAMultiplier; modifiedTradingConfig.BuyDCAMinBalance = modifiers.BuyDCAMinBalance ?? modifiedTradingConfig.BuyDCAMinBalance; modifiedTradingConfig.BuyDCASamePairTimeout = modifiers.BuyDCASamePairTimeout ?? modifiedTradingConfig.BuyDCASamePairTimeout; modifiedTradingConfig.BuyDCATrailing = modifiers.BuyDCATrailing ?? modifiedTradingConfig.BuyDCATrailing; modifiedTradingConfig.BuyDCATrailingStopMargin = modifiers.BuyDCATrailingStopMargin ?? modifiedTradingConfig.BuyDCATrailingStopMargin; modifiedTradingConfig.BuyDCATrailingStopAction = modifiers.BuyDCATrailingStopAction ?? modifiedTradingConfig.BuyDCATrailingStopAction; modifiedTradingConfig.SellEnabled = modifiers.SellEnabled ?? modifiedTradingConfig.SellEnabled; modifiedTradingConfig.SellMargin = modifiers.SellMargin ?? modifiedTradingConfig.SellMargin; modifiedTradingConfig.SellTrailing = modifiers.SellTrailing ?? modifiedTradingConfig.SellTrailing; modifiedTradingConfig.SellTrailingStopMargin = modifiers.SellTrailingStopMargin ?? modifiedTradingConfig.SellTrailingStopMargin; modifiedTradingConfig.SellTrailingStopAction = modifiers.SellTrailingStopAction ?? modifiedTradingConfig.SellTrailingStopAction; modifiedTradingConfig.SellStopLossEnabled = modifiers.SellStopLossEnabled ?? modifiedTradingConfig.SellStopLossEnabled; modifiedTradingConfig.SellStopLossAfterDCA = modifiers.SellStopLossAfterDCA ?? modifiedTradingConfig.SellStopLossAfterDCA; modifiedTradingConfig.SellStopLossMinAge = modifiers.SellStopLossMinAge ?? modifiedTradingConfig.SellStopLossMinAge; modifiedTradingConfig.SellStopLossMargin = modifiers.SellStopLossMargin ?? modifiedTradingConfig.SellStopLossMargin; modifiedTradingConfig.SellDCAMargin = modifiers.SellDCAMargin ?? modifiedTradingConfig.SellDCAMargin; modifiedTradingConfig.SellDCATrailing = modifiers.SellDCATrailing ?? modifiedTradingConfig.SellDCATrailing; modifiedTradingConfig.SellDCATrailingStopMargin = modifiers.SellDCATrailingStopMargin ?? modifiedTradingConfig.SellDCATrailingStopMargin; modifiedTradingConfig.SellDCATrailingStopAction = modifiers.SellDCATrailingStopAction ?? modifiedTradingConfig.SellDCATrailingStopAction; modifiedTradingConfig.RepeatLastDCALevel = modifiers.RepeatLastDCALevel ?? modifiedTradingConfig.RepeatLastDCALevel; modifiedTradingConfig.DCALevels = modifiers.DCALevels ?? modifiedTradingConfig.DCALevels; // Base Pair Config modifiedPairConfig.SwapEnabled = modifiers.SwapEnabled ?? modifiedPairConfig.SwapEnabled; modifiedPairConfig.SwapSignalRules = modifiers.SwapSignalRules ?? modifiedPairConfig.SwapSignalRules; modifiedPairConfig.SwapTimeout = modifiers.SwapTimeout ?? modifiedPairConfig.SwapTimeout; modifiedPairConfig.ArbitrageEnabled = modifiers.ArbitrageEnabled ?? modifiedPairConfig.ArbitrageEnabled; modifiedPairConfig.ArbitrageMarkets = modifiers.ArbitrageMarkets ?? modifiedPairConfig.ArbitrageMarkets; modifiedPairConfig.ArbitrageType = modifiers.ArbitrageType ?? modifiedPairConfig.ArbitrageType; modifiedPairConfig.ArbitrageBuyMultiplier = modifiers.ArbitrageBuyMultiplier ?? modifiedPairConfig.ArbitrageBuyMultiplier; modifiedPairConfig.ArbitrageSellMultiplier = modifiers.ArbitrageSellMultiplier ?? modifiedPairConfig.ArbitrageSellMultiplier; modifiedPairConfig.ArbitrageSignalRules = modifiers.ArbitrageSignalRules ?? modifiedPairConfig.ArbitrageSignalRules; if (oldPairConfig != null && !oldPairConfig.ArbitrageEnabled && modifiedPairConfig.ArbitrageEnabled) { signalsService.ProcessPair(pair, signals); } } appliedRules.Add(rule); if (tradingService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch) { break; } } } pairConfigs[pair] = CreatePairConfig(pair, modifiedTradingConfig, modifiedPairConfig, appliedRules); } } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TradingRulesProcessed, $"Rules: {enabledRules.Count()}, Pairs: {allPairs.Count}"); } private PairConfig CreatePairConfig(string pair, ITradingConfig modifiedTradingConfig, IPairConfig modifiedPairConfig, IEnumerable appliedRules) { ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair); DCALevel currentDCALevel = GetCurrentDCALevel(tradingPair, modifiedTradingConfig.DCALevels); DCALevel nextDCALevel = GetNextDCALevel(tradingPair, modifiedTradingConfig.DCALevels, modifiedTradingConfig.RepeatLastDCALevel); return new PairConfig { Rules = appliedRules.Select(r => r.Name), MaxPairs = modifiedTradingConfig.MaxPairs, BuyEnabled = tradingPair == null ? modifiedTradingConfig.BuyEnabled : modifiedTradingConfig.BuyDCAEnabled, BuyType = modifiedTradingConfig.BuyType, BuyMaxCost = modifiedTradingConfig.BuyMaxCost, BuyMultiplier = tradingPair == null ? (modifiedTradingConfig.BuyMultiplier != 0 ? modifiedTradingConfig.BuyMultiplier : 1) : nextDCALevel?.BuyMultiplier ?? modifiedTradingConfig.BuyDCAMultiplier, BuyMinBalance = tradingPair == null ? modifiedTradingConfig.BuyMinBalance : modifiedTradingConfig.BuyDCAMinBalance, BuySamePairTimeout = (tradingPair == null ? modifiedTradingConfig.BuySamePairTimeout : nextDCALevel?.BuySamePairTimeout ?? modifiedTradingConfig.BuyDCASamePairTimeout) / Application.Speed, BuyTrailing = tradingPair == null ? modifiedTradingConfig.BuyTrailing : nextDCALevel?.BuyTrailing ?? modifiedTradingConfig.BuyDCATrailing, BuyTrailingStopMargin = tradingPair == null ? modifiedTradingConfig.BuyTrailingStopMargin : nextDCALevel?.BuyTrailingStopMargin ?? modifiedTradingConfig.BuyDCATrailingStopMargin, BuyTrailingStopAction = tradingPair == null ? modifiedTradingConfig.BuyTrailingStopAction : nextDCALevel?.BuyTrailingStopAction ?? modifiedTradingConfig.BuyDCATrailingStopAction, SellEnabled = modifiedTradingConfig.SellEnabled, SellType = modifiedTradingConfig.SellType, SellMargin = currentDCALevel == null ? modifiedTradingConfig.SellMargin : currentDCALevel?.SellMargin ?? modifiedTradingConfig.SellDCAMargin, SellTrailing = currentDCALevel == null ? modifiedTradingConfig.SellTrailing : currentDCALevel?.SellTrailing ?? modifiedTradingConfig.SellDCATrailing, SellTrailingStopMargin = currentDCALevel == null ? modifiedTradingConfig.SellTrailingStopMargin : currentDCALevel?.SellTrailingStopMargin ?? modifiedTradingConfig.SellDCATrailingStopMargin, SellTrailingStopAction = currentDCALevel == null ? modifiedTradingConfig.SellTrailingStopAction : currentDCALevel?.SellTrailingStopAction ?? modifiedTradingConfig.SellDCATrailingStopAction, SellStopLossEnabled = modifiedTradingConfig.SellStopLossEnabled, SellStopLossAfterDCA = modifiedTradingConfig.SellStopLossAfterDCA, SellStopLossMinAge = modifiedTradingConfig.SellStopLossMinAge / Application.Speed, SellStopLossMargin = modifiedTradingConfig.SellStopLossMargin, SwapEnabled = modifiedPairConfig.SwapEnabled, SwapSignalRules = modifiedPairConfig.SwapSignalRules, SwapTimeout = (int)Math.Round(modifiedPairConfig.SwapTimeout / Application.Speed), ArbitrageEnabled = modifiedPairConfig.ArbitrageEnabled, ArbitrageMarkets = modifiedPairConfig.ArbitrageMarkets, ArbitrageType = modifiedPairConfig.ArbitrageType, ArbitrageBuyMultiplier = modifiedPairConfig.ArbitrageBuyMultiplier, ArbitrageSellMultiplier = modifiedPairConfig.ArbitrageSellMultiplier, ArbitrageSignalRules = modifiedPairConfig.ArbitrageSignalRules, CurrentDCAMargin = currentDCALevel?.Margin, NextDCAMargin = nextDCALevel?.Margin }; } private DCALevel GetCurrentDCALevel(ITradingPair tradingPair, List dcaLevels) { if (tradingPair != null && tradingPair.DCALevel > 0 && dcaLevels.Count >= tradingPair.DCALevel) { return dcaLevels[tradingPair.DCALevel - 1]; } else { return null; } } private DCALevel GetNextDCALevel(ITradingPair tradingPair, List dcaLevels, bool repeatLastDCALevel) { if (tradingPair != null && dcaLevels.Count > 0) { if (dcaLevels.Count >= tradingPair.DCALevel + 1) { return dcaLevels[tradingPair.DCALevel]; } else if (repeatLastDCALevel) { return dcaLevels[dcaLevels.Count - 1]; } else { return null; } } else { return null; } } } } ================================================ FILE: IntelliTrader.Trading/TimedTasks/TradingTimedTask.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace IntelliTrader.Trading { public class TradingTimedTask : HighResolutionTimedTask { public bool LoggingEnabled { get; set; } = true; private readonly ILoggingService loggingService; private readonly INotificationService notificationService; private readonly IHealthCheckService healthCheckService; private readonly ISignalsService signalsService; private readonly ITradingService tradingService; private readonly IOrderingService orderingService; private readonly ConcurrentDictionary trailingBuys = new ConcurrentDictionary(); private readonly ConcurrentDictionary trailingSells = new ConcurrentDictionary(); public TradingTimedTask(ILoggingService loggingService, INotificationService notificationService, IHealthCheckService healthCheckService, ISignalsService signalsService, IOrderingService orderingService, ITradingService tradingService) { this.loggingService = loggingService; this.notificationService = notificationService; this.healthCheckService = healthCheckService; this.signalsService = signalsService; this.orderingService = orderingService; this.tradingService = tradingService; } protected override void Run() { ProcessTradingPairs(); } public void InitiateBuy(BuyOptions options) { IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair); if (!options.ManualOrder && pairConfig.BuyTrailing != 0) { if (!trailingBuys.ContainsKey(options.Pair)) { StopTrailingSell(options.Pair); decimal currentPrice = tradingService.GetPrice(options.Pair); decimal currentMargin = 0; var trailingInfo = new BuyTrailingInfo { BuyOptions = options, Trailing = pairConfig.BuyTrailing, TrailingStopMargin = pairConfig.BuyTrailingStopMargin, TrailingStopAction = pairConfig.BuyTrailingStopAction, InitialPrice = currentPrice, LastTrailingMargin = currentMargin, BestTrailingMargin = currentMargin }; if (trailingBuys.TryAdd(options.Pair, trailingInfo)) { if (LoggingEnabled) { ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair); loggingService.Info($"Start trailing buy {tradingPair?.FormattedName ?? options.Pair}. " + $"Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}"); } } } } else { orderingService.PlaceBuyOrder(options); } } public void InitiateSell(SellOptions options) { if (tradingService.Account.HasTradingPair(options.Pair)) { IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair); if (!options.ManualOrder && pairConfig.SellTrailing != 0) { if (!trailingSells.ContainsKey(options.Pair)) { StopTrailingBuy(options.Pair); ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair); tradingPair.SetCurrentValues(tradingService.GetPrice(options.Pair), tradingService.Exchange.GetPriceSpread(options.Pair)); var trailingInfo = new SellTrailingInfo { SellOptions = options, SellMargin = pairConfig.SellMargin, Trailing = pairConfig.SellTrailing, TrailingStopMargin = pairConfig.SellTrailingStopMargin, TrailingStopAction = pairConfig.SellTrailingStopAction, InitialPrice = tradingPair.CurrentPrice, LastTrailingMargin = tradingPair.CurrentMargin, BestTrailingMargin = tradingPair.CurrentMargin }; if (trailingSells.TryAdd(options.Pair, trailingInfo)) { if (LoggingEnabled) { loggingService.Info($"Start trailing sell {tradingPair.FormattedName}. " + $"Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}"); } } } } else { orderingService.PlaceSellOrder(options); } } else { loggingService.Info($"Cancel initiate sell for {options.Pair}. Reason: pair does not exist"); } } public void ProcessTradingPairs() { int traidingPairsCount = 0; foreach (var tradingPair in tradingService.Account.GetTradingPairs()) { IPairConfig pairConfig = tradingService.GetPairConfig(tradingPair.Pair); tradingPair.SetCurrentValues(tradingService.GetPrice(tradingPair.Pair), tradingService.Exchange.GetPriceSpread(tradingPair.Pair)); tradingPair.Metadata.TradingRules = pairConfig.Rules.ToList(); tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null ? signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null; tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating(); if (trailingSells.TryGetValue(tradingPair.Pair, out SellTrailingInfo sellTrailingInfo)) { if (pairConfig.SellEnabled) { if (Math.Round(tradingPair.CurrentMargin, 1) != Math.Round(sellTrailingInfo.LastTrailingMargin, 1)) { if (LoggingEnabled) { loggingService.Info($"Continue trailing sell {tradingPair.FormattedName}. " + $"Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}"); } } if (tradingPair.CurrentMargin <= sellTrailingInfo.TrailingStopMargin || tradingPair.CurrentMargin < (sellTrailingInfo.BestTrailingMargin - sellTrailingInfo.Trailing)) { StopTrailingSell(tradingPair.Pair); if (tradingPair.CurrentMargin > 0 || sellTrailingInfo.SellMargin < 0) { if (sellTrailingInfo.TrailingStopAction == SellTrailingStopAction.Sell || tradingPair.CurrentMargin > sellTrailingInfo.TrailingStopMargin) { orderingService.PlaceSellOrder(sellTrailingInfo.SellOptions); } else { if (LoggingEnabled) { loggingService.Info($"Stop trailing sell {tradingPair.FormattedName}. Reason: stop margin reached"); } } } else { if (LoggingEnabled) { loggingService.Info($"Stop trailing sell {tradingPair.FormattedName}. Reason: negative margin"); } } } else { sellTrailingInfo.LastTrailingMargin = tradingPair.CurrentMargin; if (tradingPair.CurrentMargin > sellTrailingInfo.BestTrailingMargin) { sellTrailingInfo.BestTrailingMargin = tradingPair.CurrentMargin; } } } else { StopTrailingSell(tradingPair.Pair); } } else { if (pairConfig.SellEnabled && tradingPair.CurrentMargin >= pairConfig.SellMargin) { InitiateSell(new SellOptions(tradingPair.Pair)); } else if (pairConfig.SellEnabled && pairConfig.SellStopLossEnabled && tradingPair.CurrentMargin <= pairConfig.SellStopLossMargin && tradingPair.CurrentAge >= pairConfig.SellStopLossMinAge && (pairConfig.NextDCAMargin == null || !pairConfig.SellStopLossAfterDCA)) { if (LoggingEnabled) { loggingService.Info($"Stop loss triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}"); } orderingService.PlaceSellOrder(new SellOptions(tradingPair.Pair)); } else if (pairConfig.NextDCAMargin != null && pairConfig.BuyEnabled && pairConfig.NextDCAMargin != null && !trailingBuys.ContainsKey(tradingPair.Pair) && !trailingSells.ContainsKey(tradingPair.Pair)) { if (tradingPair.CurrentMargin <= pairConfig.NextDCAMargin) { var buyOptions = new BuyOptions(tradingPair.Pair) { MaxCost = tradingPair.Cost * pairConfig.BuyMultiplier, IgnoreExisting = true }; if (tradingService.CanBuy(buyOptions, message: out string message)) { if (LoggingEnabled) { loggingService.Info($"DCA triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}, " + $"Level: {pairConfig.NextDCAMargin:0.00}, Multiplier: {pairConfig.BuyMultiplier}"); } InitiateBuy(buyOptions); } } } } traidingPairsCount++; } foreach (var kvp in trailingBuys) { string pair = kvp.Key; BuyTrailingInfo buyTrailingInfo = kvp.Value; ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair); IPairConfig pairConfig = tradingService.GetPairConfig(pair); decimal currentPrice = tradingService.GetPrice(pair); decimal currentMargin = Utils.CalculatePercentage(buyTrailingInfo.InitialPrice, currentPrice); if (pairConfig.BuyEnabled) { if (Math.Round(currentMargin, 1) != Math.Round(buyTrailingInfo.LastTrailingMargin, 1)) { if (LoggingEnabled) { loggingService.Info($"Continue trailing buy {tradingPair?.FormattedName ?? pair}. Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}"); } } if (currentMargin >= buyTrailingInfo.TrailingStopMargin || currentMargin > (buyTrailingInfo.BestTrailingMargin - buyTrailingInfo.Trailing)) { StopTrailingBuy(pair); if (buyTrailingInfo.TrailingStopAction == BuyTrailingStopAction.Buy || currentMargin < buyTrailingInfo.TrailingStopMargin) { orderingService.PlaceBuyOrder(buyTrailingInfo.BuyOptions); } else { if (LoggingEnabled) { loggingService.Info($"Stop trailing buy {tradingPair?.FormattedName ?? pair}. Reason: stop margin reached"); } } } else { buyTrailingInfo.LastTrailingMargin = currentMargin; if (currentMargin < buyTrailingInfo.BestTrailingMargin) { buyTrailingInfo.BestTrailingMargin = currentMargin; } } } else { StopTrailingBuy(pair); } } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TradingPairsProcessed, $"Pairs: {traidingPairsCount}, Trailing buys: {trailingBuys.Count}, Trailing sells: {trailingSells.Count}"); } public List GetTrailingBuys() { return trailingBuys.Keys.ToList(); } public List GetTrailingSells() { return trailingSells.Keys.ToList(); } public void StopTrailing() { trailingBuys.Clear(); trailingSells.Clear(); } public void StopTrailingBuy(string pair) { trailingBuys.TryRemove(pair, out BuyTrailingInfo buyTrailingInfo); } public void StopTrailingSell(string pair) { trailingSells.TryRemove(pair, out SellTrailingInfo sellTrailingInfo); } } } ================================================ FILE: IntelliTrader.Web/AppModule.cs ================================================ using Autofac; using IntelliTrader.Core; namespace IntelliTrader.Web { public class AppModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().As().Named(Constants.ServiceNames.WebService).SingleInstance(); } } } ================================================ FILE: IntelliTrader.Web/Controllers/HomeController.cs ================================================ using IntelliTrader.Core; using IntelliTrader.Web.Models; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using System; using System.Linq; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Authorization; using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.Extensions.Primitives; using System.Threading.Tasks; using System.Security.Cryptography; using System.Text; namespace IntelliTrader.Web.Controllers { [Authorize] public class HomeController : Controller { #region Authentication [AllowAnonymous] public async Task Login() { var coreService = Application.Resolve(); if (coreService.Config.PasswordProtected) { var model = new LoginViewModel { RememberMe = true }; return View(model); } else { return await PerformLogin(true); } } [HttpPost] [AllowAnonymous] public async Task Login(LoginViewModel model) { if (ModelState.IsValid) { var coreService = Application.Resolve(); var isValid = !coreService.Config.PasswordProtected || ComputeMD5Hash(model.Password).Equals(coreService.Config.Password, StringComparison.InvariantCultureIgnoreCase); if (!isValid) { ModelState.AddModelError("Password", "Invalid Password"); return View(model); } else { return await PerformLogin(model.RememberMe); } } else { return View(model); } } [AllowAnonymous] public async Task Logout() { await HttpContext.SignOutAsync(); return RedirectToAction(nameof(Login)); } private async Task PerformLogin(bool persistent) { var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role); var name = "user"; identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, name)); identity.AddClaim(new Claim(ClaimTypes.Name, name)); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = persistent }); if (Request.Query.TryGetValue("ReturnUrl", out StringValues url)) { return RedirectToAction(url); } else { return RedirectToAction(nameof(Index)); } } private string ComputeMD5Hash(string input) { if (string.IsNullOrEmpty(input)) { throw new ArgumentNullException(nameof(input)); } using (var md5 = MD5.Create()) { byte[] inputBytes = Encoding.ASCII.GetBytes(input); byte[] hash = md5.ComputeHash(inputBytes); var sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { sb.Append(hash[i].ToString("X2")); } return sb.ToString(); } } #endregion Authentication public IActionResult Index() { return Dashboard(); } public IActionResult Dashboard() { var coreService = Application.Resolve(); var webService = Application.Resolve(); var model = new DashboardViewModel { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode }; return View(nameof(Dashboard), model); } public IActionResult Market() { var coreService = Application.Resolve(); var webService = Application.Resolve(); var model = new MarketViewModel { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode }; return View(model); } public IActionResult Stats() { var coreService = Application.Resolve(); var webService = Application.Resolve(); var tradingService = Application.Resolve(); var accountInitialBalance = tradingService.Config.VirtualTrading ? tradingService.Config.VirtualAccountInitialBalance : tradingService.Config.AccountInitialBalance; var accountInitialBalanceDate = tradingService.Config.VirtualTrading ? DateTimeOffset.Now.AddDays(-30) : tradingService.Config.AccountInitialBalanceDate; var model = new StatsViewModel { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode, TimezoneOffset = coreService.Config.TimezoneOffset, AccountInitialBalance = accountInitialBalance, AccountBalance = tradingService.Account.GetTotalBalance(), Market = tradingService.Config.Market, Balances = new Dictionary(), Trades = GetTrades() }; foreach (var kvp in model.Trades.OrderBy(k => k.Key)) { var date = kvp.Key; var trades = kvp.Value; model.Balances[date] = accountInitialBalance; if (date > accountInitialBalanceDate.Date) { for (int d = 1; d < (int)(date - accountInitialBalanceDate.Date).TotalDays; d++) { var prevDate = date.AddDays(-d); if (model.Trades.ContainsKey(prevDate)) { model.Balances[date] += model.Trades[prevDate].Where(t => !t.IsSwap).Sum(t => t.Profit); } } } } return View(model); } public IActionResult Rules() { var allTades = GetTrades(); var signalRuleStats = new Dictionary(); foreach (var trade in allTades.Values.SelectMany(t => t)) { if (trade.IsSuccessful) { var signalRule = trade?.Metadata?.SignalRule; if (!String.IsNullOrWhiteSpace(signalRule)) { if (!signalRuleStats.TryGetValue(signalRule, out SignalRuleStats ruleStats)) { ruleStats = new SignalRuleStats(); signalRuleStats.Add(signalRule, ruleStats); } if (!trade.IsSwap) { ruleStats.TotalCost += trade.Cost; ruleStats.TotalProfit += trade.Profit; decimal margin = trade.Profit / (trade.Cost + (trade.Metadata?.AdditionalCosts ?? 0)) * 100; if (trade.OrderDates.Count == 1) { ruleStats.Margin.Add(margin); } else { ruleStats.MarginDCA.Add(margin); } } else { ruleStats.TotalSwaps++; } ruleStats.TotalTrades++; ruleStats.TotalOrders += trade.OrderDates.Count; ruleStats.TotalFees += trade.FeesTotal; ruleStats.Age.Add((trade.SellDate - trade.OrderDates.Min()).TotalDays); ruleStats.DCA.Add((trade.OrderDates.Count - 1) + (trade.Metadata?.AdditionalDCALevels ?? 0)); } } } var coreService = Application.Resolve(); var webService = Application.Resolve(); var model = new RulesViewModel { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode, SignalRuleStats = signalRuleStats }; return View(model); } public IActionResult Trades(DateTimeOffset id) { var coreService = Application.Resolve(); var webService = Application.Resolve(); var model = new TradesViewModel() { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode, TimezoneOffset = coreService.Config.TimezoneOffset, Date = id, Trades = GetTrades(id).Values.FirstOrDefault() ?? new List() }; return View(model); } public IActionResult Settings() { var coreService = Application.Resolve(); var webService = Application.Resolve(); var tradingService = Application.Resolve(); var allConfigurableServices = Application.Resolve>(); var model = new SettingsViewModel() { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode, BuyEnabled = tradingService.Config.BuyEnabled, BuyDCAEnabled = tradingService.Config.BuyDCAEnabled, SellEnabled = tradingService.Config.SellEnabled, TradingSuspended = tradingService.IsTradingSuspended, HealthCheckEnabled = coreService.Config.HealthCheckEnabled, 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)) }; return View(model); } public IActionResult Log() { var coreService = Application.Resolve(); var webService = Application.Resolve(); var loggingService = Application.Resolve(); var model = new LogViewModel() { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode, LogEntries = loggingService.GetLogEntries().Reverse().Take(500) }; return View(model); } public IActionResult Help() { var coreService = Application.Resolve(); var webService = Application.Resolve(); var model = new HelpViewModel() { InstanceName = coreService.Config.InstanceName, Version = coreService.Version, ReadOnlyMode = webService.Config.ReadOnlyMode }; return View(model); } public IActionResult Status() { var loggingService = Application.Resolve(); var tradingService = Application.Resolve(); var signalsService = Application.Resolve(); var healthCheckService = Application.Resolve(); var status = new { Balance = tradingService.Account.GetBalance(), GlobalRating = signalsService.GetGlobalRating()?.ToString("0.000") ?? "N/A", TrailingBuys = tradingService.GetTrailingBuys(), TrailingSells = tradingService.GetTrailingSells(), TrailingSignals = signalsService.GetTrailingSignals(), TradingSuspended = tradingService.IsTradingSuspended, HealthChecks = healthCheckService.GetHealthChecks().OrderBy(c => c.Name), LogEntries = loggingService.GetLogEntries().Reverse().Take(5) }; return Json(status); } public IActionResult SignalNames() { var signalsService = Application.Resolve(); return Json(signalsService.GetSignalNames()); } [HttpPost] public IActionResult TradingPairs() { var coreService = Application.Resolve(); var tradingService = Application.Resolve(); var tradingPairs = from tradingPair in tradingService.Account.GetTradingPairs() let pairConfig = tradingService.GetPairConfig(tradingPair.Pair) select new { Name = tradingPair.Pair, DCA = tradingPair.DCALevel, TradingViewName = $"{tradingService.Config.Exchange.ToUpperInvariant()}:{tradingPair.Pair}", Margin = tradingPair.CurrentMargin.ToString("0.00"), Target = pairConfig.SellMargin.ToString("0.00"), CurrentPrice = tradingPair.CurrentPrice.ToString("0.00000000"), CurrentSpread = tradingPair.CurrentSpread.ToString("0.00"), BoughtPrice = tradingPair.AveragePrice.ToString("0.00000000"), Cost = tradingPair.Cost.ToString("0.00000000"), CurrentCost = tradingPair.CurrentCost.ToString("0.00000000"), Amount = tradingPair.Amount.ToString("0.########"), OrderDates = tradingPair.OrderDates.Select(d => d.ToOffset(TimeSpan.FromHours(coreService.Config.TimezoneOffset)).ToString("yyyy-MM-dd HH:mm:ss")), OrderIds = tradingPair.OrderIds, Age = tradingPair.CurrentAge.ToString("0.00"), CurrentRating = tradingPair.Metadata.CurrentRating?.ToString("0.000") ?? "N/A", BoughtRating = tradingPair.Metadata.BoughtRating?.ToString("0.000") ?? "N/A", SignalRule = tradingPair.Metadata.SignalRule ?? "N/A", SwapPair = tradingPair.Metadata.SwapPair, TradingRules = pairConfig.Rules, IsTrailingSell = tradingService.GetTrailingSells().Contains(tradingPair.Pair), IsTrailingBuy = tradingService.GetTrailingBuys().Contains(tradingPair.Pair), LastBuyMargin = tradingPair.Metadata.LastBuyMargin?.ToString("0.00") ?? "N/A", Config = pairConfig }; return Json(tradingPairs); } [HttpPost] public IActionResult MarketPairs(List signalsFilter) { var coreService = Application.Resolve(); var tradingService = Application.Resolve(); var signalsService = Application.Resolve(); var allSignals = signalsService.GetAllSignals(); if (allSignals != null) { if (signalsFilter.Count > 0) { allSignals = allSignals.Where(s => signalsFilter.Contains(s.Name)); } var groupedSignals = allSignals.GroupBy(s => s.Pair).ToDictionary(g => g.Key, g => g.AsEnumerable()); var marketPairs = from signalGroup in groupedSignals let pair = signalGroup.Key let pairConfig = tradingService.GetPairConfig(pair) select new { Name = pair, TradingViewName = $"{tradingService.Config.Exchange.ToUpperInvariant()}:{pair}", VolumeList = signalGroup.Value.Select(s => new { s.Name, s.Volume }), VolumeChangeList = signalGroup.Value.Select(s => new { s.Name, s.VolumeChange }), Price = tradingService.GetPrice(pair).ToString("0.00000000"), PriceChangeList = signalGroup.Value.Select(s => new { s.Name, s.PriceChange }), RatingList = signalGroup.Value.Select(s => new { s.Name, s.Rating }), RatingChangeList = signalGroup.Value.Select(s => new { s.Name, s.RatingChange }), VolatilityList = signalGroup.Value.Select(s => new { s.Name, s.Volatility }), Spread = tradingService.Exchange.GetPriceSpread(pair).ToString("0.00"), ArbitrageList = from market in Enum.GetNames(typeof(ArbitrageMarket)).Where(m => m != tradingService.Config.Market) let arbitrage = tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, new List { Enum.Parse(market) }) select new { Name = $"{arbitrage.Market}-{arbitrage.Type.ToString()[0]}", Arbitrage = arbitrage.IsAssigned ? arbitrage.Percentage.ToString("0.00") : "N/A" }, SignalRules = signalsService.GetTrailingInfo(pair)?.Select(ti => ti.Rule.Name) ?? new string[0], HasTradingPair = tradingService.Account.HasTradingPair(pair), Config = pairConfig }; return Json(marketPairs); } else { return Json(null); } } [HttpPost] public IActionResult Settings(SettingsViewModel model) { if (!Application.Resolve().Config.ReadOnlyMode) { var coreService = Application.Resolve(); var tradingService = Application.Resolve(); coreService.Config.HealthCheckEnabled = model.HealthCheckEnabled; tradingService.Config.BuyEnabled = model.BuyEnabled; tradingService.Config.BuyDCAEnabled = model.BuyDCAEnabled; tradingService.Config.SellEnabled = model.SellEnabled; if (model.TradingSuspended) { tradingService.SuspendTrading(); } else { tradingService.ResumeTrading(); } return Settings(); } else { return Settings(); } } [HttpPost] public IActionResult SaveConfig() { string configName = Request.Form["name"].ToString(); string configDefinition = Request.Form["definition"].ToString(); if (!Application.Resolve().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(configName) && !String.IsNullOrWhiteSpace(configDefinition)) { Application.ConfigProvider.SetSectionJson(configName, configDefinition); return new OkResult(); } else { return new BadRequestResult(); } } [HttpPost] public IActionResult Sell() { string pair = Request.Form["pair"].ToString(); if (!Application.Resolve().Config.ReadOnlyMode && pair != null && decimal.TryParse(Request.Form["amount"], out decimal amount) && amount > 0) { var tradingService = Application.Resolve(); tradingService.Sell(new SellOptions(pair) { Amount = amount, ManualOrder = true }); return new OkResult(); } else { return new BadRequestResult(); } } [HttpPost] public IActionResult Buy() { string pair = Request.Form["pair"].ToString(); if (!Application.Resolve().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(pair) && decimal.TryParse(Request.Form["amount"], out decimal amount) && amount > 0) { var tradingService = Application.Resolve(); tradingService.Buy(new BuyOptions(pair) { Amount = amount, IgnoreExisting = true, ManualOrder = true }); return new OkResult(); } else { return new BadRequestResult(); } } [HttpPost] public IActionResult BuyDefault() { string pair = Request.Form["pair"].ToString(); if (!Application.Resolve().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(pair)) { var signalsService = Application.Resolve(); var tradingService = Application.Resolve(); tradingService.Buy(new BuyOptions(pair) { MaxCost = tradingService.GetPairConfig(pair).BuyMaxCost, IgnoreExisting = true, ManualOrder = true, Metadata = new OrderMetadata { BoughtGlobalRating = signalsService.GetGlobalRating() } }); return new OkResult(); } else { return new BadRequestResult(); } } [HttpPost] public IActionResult Swap() { string pair = Request.Form["pair"].ToString(); string swap = Request.Form["swap"].ToString(); if (!Application.Resolve().Config.ReadOnlyMode && !String.IsNullOrWhiteSpace(pair) && !String.IsNullOrWhiteSpace(swap)) { var tradingService = Application.Resolve(); tradingService.Swap(new SwapOptions(pair, swap, new OrderMetadata()) { ManualOrder = true }); return new OkResult(); } else { return new BadRequestResult(); } } public IActionResult RefreshAccount() { if (!Application.Resolve().Config.ReadOnlyMode) { var tradingService = Application.Resolve(); tradingService.Account.Refresh(); return new OkResult(); } else { return new BadRequestResult(); } } public IActionResult RestartServices() { if (!Application.Resolve().Config.ReadOnlyMode) { var coreService = Application.Resolve(); coreService.Restart(); return new OkResult(); } else { return new BadRequestResult(); } } private Dictionary> GetTrades(DateTimeOffset? date = null) { var coreService = Application.Resolve(); var logsPath = Path.Combine(Directory.GetCurrentDirectory(), "log"); var tradeResultPattern = new Regex($"{nameof(TradeResult)} (?\\{{.*\\}})", RegexOptions.Compiled); var trades = new Dictionary>(); if (Directory.Exists(logsPath)) { foreach (var tradesLogFilePath in Directory.EnumerateFiles(logsPath, "*-trades.txt", SearchOption.TopDirectoryOnly)) { IEnumerable logLines = Utils.ReadAllLinesWriteSafe(tradesLogFilePath); foreach (var logLine in logLines) { var match = tradeResultPattern.Match(logLine); if (match.Success) { var data = match.Groups["data"].ToString(); var json = Utils.FixInvalidJson(data.Replace(nameof(OrderMetadata), "")) .Replace("AveragePricePaid", nameof(ITradeResult.AveragePrice)); // Old property migration TradeResult tradeResult = JsonConvert.DeserializeObject(json); if (tradeResult.IsSuccessful && tradeResult.Metadata?.IsTransitional != true) { DateTimeOffset tradeDate = tradeResult.SellDate.ToOffset(TimeSpan.FromHours(coreService.Config.TimezoneOffset)).Date; if (date == null || date == tradeDate) { if (!trades.ContainsKey(tradeDate)) { trades.Add(tradeDate, new List()); } trades[tradeDate].Add(tradeResult); } } } } } } return trades; } } } ================================================ FILE: IntelliTrader.Web/IntelliTrader.Web.csproj ================================================ netcoreapp2.1 false Library 2.8 PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest $(IncludeRazorContentInPack) ================================================ FILE: IntelliTrader.Web/Misc/Utils.cs ================================================ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; namespace IntelliTrader.Web { public static class Utils { private static Regex fixJsonPattern = new Regex(@"\w[^,{]+[\w""]", RegexOptions.Compiled); public static string FixInvalidJson(string json) { string fixedJson = fixJsonPattern.Replace(json, match => { string matchString = match.ToString(); if (!matchString.EndsWith("\"")) { string[] split = matchString.Split(": "); if (split.Length == 1) { return $"\"{matchString.Trim('"', ' ')}\""; } else { string left = split[0].Trim('"', ' '); string right = split[1].Trim('"', ' '); if (right[0] == '[') { return $"\"{left}\": {right.Replace("[", "[\"")}\""; } else if (right == "True" || right == "False") { return $"\"{left}\": \"{right.ToLowerInvariant()}\""; } else if (right == "null") { return $"\"{left}\": {right}"; } else { return $"\"{left}\": \"{right}\""; } } } else { return matchString; } }); return fixedJson; } public static IEnumerable ReadAllLinesWriteSafe(string path) { using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var sr = new StreamReader(fs)) { while (!sr.EndOfStream) { yield return sr.ReadLine(); } } } } } ================================================ FILE: IntelliTrader.Web/Models/BaseViewModel.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class BaseViewModel { public string InstanceName { get; set; } public string Version { get; set; } public bool ReadOnlyMode { get; set; } } } ================================================ FILE: IntelliTrader.Web/Models/Config/WebConfig.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Text; namespace IntelliTrader.Web { internal class WebConfig : IWebConfig { public bool Enabled { get; set; } public bool DebugMode { get; set; } public bool ReadOnlyMode { get; set; } public int Port { get; set; } public bool SSLEnabled { get; set; } public string SSLCertPath { get; set; } public string SSLCertPassword { get; set; } } } ================================================ FILE: IntelliTrader.Web/Models/DashboardViewModel.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class DashboardViewModel : BaseViewModel { } } ================================================ FILE: IntelliTrader.Web/Models/HelpViewModel.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class HelpViewModel : BaseViewModel { } } ================================================ FILE: IntelliTrader.Web/Models/LogViewModel.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class LogViewModel : BaseViewModel { public IEnumerable LogEntries { get; set; } } } ================================================ FILE: IntelliTrader.Web/Models/LoginViewModel.cs ================================================ using System.ComponentModel.DataAnnotations; namespace IntelliTrader.Web.Models { public class LoginViewModel : BaseViewModel { [Required, DataType(DataType.Password)] public string Password { get; set; } public bool RememberMe { get; set; } } } ================================================ FILE: IntelliTrader.Web/Models/MarketViewModel.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class MarketViewModel : BaseViewModel { } } ================================================ FILE: IntelliTrader.Web/Models/RulesViewModel.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class RulesViewModel : BaseViewModel { public Dictionary SignalRuleStats { get; set; } } public class SignalRuleStats { public decimal TotalProfit { get; set; } public decimal TotalFees { get; set; } public decimal TotalCost { get; set; } public int TotalTrades { get; set; } public int TotalOrders { get; set; } public int TotalSwaps { get; set; } public List Age { get; set; } = new List(); public List Margin { get; set; } = new List(); public List MarginDCA { get; set; } = new List(); public List DCA { get; set; } = new List(); } } ================================================ FILE: IntelliTrader.Web/Models/SettingsViewModel.cs ================================================ using IntelliTrader.Core; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class SettingsViewModel : BaseViewModel { [Display(Name = "Buy Enabled")] public bool BuyEnabled { get; set; } [Display(Name = "Buy DCA Enabled")] public bool BuyDCAEnabled { get; set; } [Display(Name = "Sell Enabled")] public bool SellEnabled { get; set; } [Display(Name = "Trading Suspended")] public bool TradingSuspended { get; set; } [Display(Name = "Health Check Enabled")] public bool HealthCheckEnabled { get; set; } public Dictionary Configs { get; set; } } } ================================================ FILE: IntelliTrader.Web/Models/StatsViewModel.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class StatsViewModel : BaseViewModel { public double TimezoneOffset { get; set; } public decimal AccountInitialBalance { get; set; } public decimal AccountBalance { get; set; } public string Market { get; set; } public Dictionary> Trades { get; set; } public Dictionary Balances { get; set; } } } ================================================ FILE: IntelliTrader.Web/Models/TradesViewModel.cs ================================================ using IntelliTrader.Core; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IntelliTrader.Web.Models { public class TradesViewModel : BaseViewModel { public double TimezoneOffset { get; set; } public DateTimeOffset Date { get; set; } public List Trades { get; set; } } } ================================================ FILE: IntelliTrader.Web/Properties/PublishProfiles/FolderProfile.pubxml ================================================ FileSystem FileSystem Release Any CPU True False 802ff51c-e66d-44bc-b26f-c72c68ed9e56 ..\Publish\bin False ================================================ FILE: IntelliTrader.Web/Services/WebService.cs ================================================ using IntelliTrader.Core; using Microsoft.AspNetCore.Hosting; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; namespace IntelliTrader.Web { internal class WebService : ConfigrableServiceBase, IWebService { public override string ServiceName => Constants.ServiceNames.WebService; IWebConfig IWebService.Config => Config; private readonly ILoggingService loggingService; private IWebHost webHost; public WebService(ILoggingService loggingService) { this.loggingService = loggingService; } public void Start() { loggingService.Info($"Start Web service (Port: {Config.Port})..."); try { var contentRoot = Path.GetFullPath(Directory.GetCurrentDirectory() + @"/../IntelliTrader.Web"); #if RELEASE if (!System.Diagnostics.Debugger.IsAttached) { contentRoot = Path.Combine(Directory.GetCurrentDirectory(), "bin"); } #endif var webHostBuilder = new WebHostBuilder() .UseContentRoot(contentRoot) .UseStartup() .UseKestrel(options => { if (Config.SSLEnabled) { options.Listen(IPAddress.Any, Config.Port, listenOptions => { listenOptions.UseHttps(Path.Combine(Directory.GetCurrentDirectory(), Config.SSLCertPath), Config.SSLCertPassword); }); } else { options.Listen(IPAddress.Any, Config.Port); } }); if (Config.DebugMode) { webHostBuilder.UseEnvironment("Development"); } else { webHostBuilder.UseEnvironment("Production"); } webHost = webHostBuilder.Build(); // Suppress WebHost startup messages var consOut = Console.Out; webHost.Start(); Console.SetOut(consOut); } catch (Exception ex) { loggingService.Error($"Unable to start Web service", ex); } loggingService.Info($"Web service started"); } public void Stop() { loggingService.Info($"Stop Web service..."); try { webHost.Dispose(); loggingService.Info($"Web service stopped"); } catch (Exception ex) { loggingService.Error($"Unable to stop Web service", ex); } } } } ================================================ FILE: IntelliTrader.Web/Startup.cs ================================================ using IntelliTrader.Core; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Newtonsoft.Json.Serialization; using System; using System.IO; namespace IntelliTrader.Web { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { var coreService = Application.Resolve(); services.AddAuthentication(options => { options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }).AddCookie(options => { options.LoginPath = "/Login"; options.Cookie.Name = $"{nameof(IntelliTrader)}_{coreService.Config.InstanceName}"; }); services.AddMvc().AddJsonOptions(opts => { opts.SerializerSettings.ContractResolver = new DefaultContractResolver(); }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider( Path.Combine(env.ContentRootPath, "Static")), RequestPath = "/Static" }); app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( "Default", "{action}/{id?}", new { controller = "Home", action = "Index" } ); }); } } } ================================================ FILE: IntelliTrader.Web/Static/Help/config.json ================================================ { "title": "", "additionalFooterText": "", "useSideMenu": true, "lineBreaks": "gfm", "anchorCharacter": "¶" } ================================================ FILE: IntelliTrader.Web/Static/Help/index.md ================================================ Help =========== Configuration ------------- All changes to the configuration files will take effect immediately. Configuration quick links: [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) #### Value Types |Type|Example|Description| |-|:-:|-| |Number|0.40|Numeric value| |Boolean|true|Boolean value (true/false)| |String|Market|String value| |Array|[ "BNBBTC", "ETHBTC" ] |Array of numbers, booleans or strings| |Object|{ "Key": "Value" }|Json object| #### Core Configuration |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |DebugMode|Boolean|true|Enable / disable debug mode| |PasswordProtected|Boolean|true|Use password authentication| |Password|String|MD5 hash|MD5-encrypted password| |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)| |TimezoneOffset|Number|1|Timezone offset from UTC (in hours), used by stats| |HealthCheckEnabled|Boolean|true|Enable / disable health check| |HealthCheckInterval|Number|180|Interval to check that all the data is up to date and services are running correctly (in seconds)| |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 |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| #### Web Configuration Read more about how web interface works in the [web](#Web_Interface) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable web interface| |DebugMode|Boolean|true|Enable / disable debug mode| |ReadOnlyMode|Boolean|false|Enable / disable read only mode| |Port|Number|7000|Port on which to host the web interface| |SSLEnabled|Boolean|false|Enable SSL for web interface| |SSLCertPath|String|data/cert.pfx|Path to the SSL certificate| |SSLCertPassword|String|certpass|Certificate password| #### Signals Configuration Read more about how signals work in the [signals](#Signals) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable signals| |GlobalRatingSignals|Array|"TV-5m","TV-15m","TV-1h"|Signals to calculate the Global Rating from| |Definitions|Array|[Signal Definitions](#Signal_Definitions)|Signal source definitions| ###### Signal Definitions |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Name|String|"TV-15m"|Signal name| |Receiver|String|"TradingViewCryptoSignalReceiver"|Signal receiver name| |Configuration|Object|[Signal Receiver Configuration](#Signal_Receiver_Configuration)|Signal receiver configuration| ###### Signal Receiver Configuration |Setting|Type|Default Value|Description| |-|:-:|-|-| |PollingInterval|Number|7|How often to check for latest signals (in seconds)| |SignalPeriod|Number|15|Signal period (in minutes). Possible values: 5, 15, 60, 240, 1440| |VolatilityPeriod|String|"Day"|Volatility period. Possible values: Day, Week, Month| |RequestUrl|String|"https://scanner.tradingview.com/crypto/scan"|Request url| |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| #### Trading Configuration Read more about how trading works in the [trading](#Trading) section. ###### General |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable trading| |Market|String|"BTC"|Market to trade on - BTC, ETH, USDT, etc.| |Exchange|String|"Binance"|Exchange to trade on| |MaxPairs|Number|16|Maximum pairs to trade with| |MinCost|Number|0.000999|Ignore pairs with the specified market value or lower (dust)| |ExcludedPairs|Array|[ "BNBBTC" ]|Pairs excluded from trading| |TradePriceType|String|"Last"|Price type to use for trading. Available values: Last, Ask, Bid| ###### Buying |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |BuyEnabled|Boolean|true|Enable / disable buying| |BuyType|String|"Market"|Supported types: Market. Limit is not currently supported| |BuyMaxCost|Number|0.0012|Maximum cost when buying a new pair| |BuyMultiplier|Number|1|Maximum cost multiplier| |BuyMinBalance|Number|0|Minimum account balance to buy new pairs| |BuySamePairTimeout|Number|900|Rebuy same pair timeout (in seconds)| |BuyTrailing|Number|-0.15|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)| |BuyTrailingStopMargin|Number|0.05|Stop trailing and place buy order immediately when margin hits the specified value| |BuyTrailingStopAction|String|"Buy"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel| ###### Buying DCA |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |BuyDCAEnabled|Boolean|true|Enable / disable buying DCA| |BuyDCAMultiplier|Number|1|Current cost multiplier. To double down set to 1| |BuyDCAMinBalance|Number|0|Minimum account balance to DCA| |BuyDCASamePairTimeout|Number|4200|Rebuy same pair timeout (in seconds)| |BuyDCATrailing|Number|-1.50|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)| |BuyDCATrailingStopMargin|Number|0.40|Stop trailing and place buy order immediately when margin hits the specified value| |BuyDCATrailingStopAction|String|"Cancel"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel| ###### Selling |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |SellEnabled|Boolean|true|Enable / disable selling| |SellType|String|"Market"|Supported types: Market. Limit is not currently supported| |SellMargin|Number|2.00|Minimum percentage increase to start trailing| |SellTrailing|Number|0.70|Sell trailing percentage (should be a positive number, set to 0 to disable trailing)| |SellTrailingStopMargin|Number|1.70|Stop trailing and place sell order immediately when margin hits the specified value| |SellTrailingStopAction|String|"Sell"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel| |SellStopLossEnabled|Boolean|false|Enable / disable stop loss trigger| |SellStopLossAfterDCA|Boolean|true|Trigger stop loss only after all DCA levels has been reached| |SellStopLossMinAge|Number|3.0|Minimum number of days needed before triggering the stop loss| |SellStopLossMargin|Number|-20|Trigger stop loss and immediately place sell order at the specified percentage decrease| ###### Selling DCA |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |SellDCAMargin|Number|1.50|Minimum percentage increase to start trailing| |SellDCATrailing|Number|0.50|Sell trailing percentage (set to 0 to disable trailing) |SellDCATrailingStopMargin|Number|1.25|Stop trailing and place sell order immediately when margin hits the specified value| |SellDCATrailingStopAction|String|"Sell"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel| |RepeatLastDCALevel|Boolean|fasle|Repeat the last DCA Level indefinitely, essentially making the DCA level number unlimited| |DCALevels|Array|[DCA Levels](#Dca_Levels)|Action to take after hitting the StopMargin. Possible values: Sell, Cancel| ###### DCA Levels Read more about how DCA works in the [DCA](#Dca) section. DCALevels 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. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Margin|Number|-1.50|When to trigger the DCA| |BuyMultiplier|Number|BuyDCAMultiplier|Current cost multiplier. To double down set to 1 (Optional)| |BuySamePairTimeout|Number|BuyDCASamePairTimeout|Rebuy same pair timeout (in seconds) (Optional)| |BuyTrailing|Number|BuyDCATrailing|Buy trailing percentage (should be a negative number, set to 0 to disable trailing) (Optional)| |BuyTrailingStopMargin|Number|BuyDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)| |BuyTrailingStopAction|String|BuyDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Buy, Cancel (Optional)| |SellMargin|Number|SellDCAMargin|Minimum percentage increase to start trailing (Optional)| |SellTrailing|Number|SellDCATrailing|Sell trailing percentage (set to 0 to disable trailing) (Optional)| |SellTrailingStopMargin|Number|SellDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)| |SellTrailingStopAction|String|SellDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Sell, Cancel (Optional)| ###### Accounts |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |TradingCheckInterval|Number|1|Tickers check frequency (in seconds)| |AccountRefreshInterval|Number|360|Exchange account refresh interval (in seconds)| |AccountInitialBalance|Number|0.12|Initial balance on the account, used for stats calculations| |AccountInitialBalanceDate|String|"2018-04-08T00:00:00+00:00"|Date of the initial balance snapshot| |AccountFilePath|String|"data/exchange-account.json"|Path to the account file| |VirtualTrading|Boolean|true|Enable / disable virtual trading| |VirtualTradingFees|Number|0.0005|Trading fees (percentage)| |VirtualAccountInitialBalance|Number|0.12|Initial balance on the virtual account, used for stats calculations| |VirtualAccountFilePath|String|"data/virtual-account.json"|Path to the virtual account file| #### Rules Configuration Read more about how rules work in the [rules](#Rules) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Modules|Array|[Module](#Module)|Rule Modules| ###### Module |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Module|String|"Module"|Module name| |Configuration|Object|[Module Configuration](#Module_Configuration)|Module configuration| |Entries|Array|[Rules Configuration](#Rules_Configuration)|Rules configuration| ###### Module Configuration |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |ProcessingMode|String|"AllMatches"|Available modes: AllMatches (all enabled rules will get processed) or FirstMatch (stop processing at the first rule that is satisfied)| |CheckInterval|Number|3|Rules check frequency (in seconds)| ###### Rules Configuration |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enaled|Boolean|true|Enable / disable rule| |Name|String|Default|Rule name| |Modifiers|Object|[Rule Modifiers](#Rule_Modifiers)|Rule modifiers| |Conditions|Array|[Rule Conditions](#Rule_Conditions)|Rule conditions| |Trailing|Object|[Rule trailing](#Rule_Trailing)|Rule trailing| ###### Rule Modifiers All modifiers (both signal and trading) are optional and can be omitted. Signal modifiers currently only support the *CostMultiplier* (MaxCost multiplier) Trading modifiers support any trading setting that begins with Buy, Sell plus DCALevels and MaxPairs in addition to the following trading-rule specific modifiers: |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |SwapEnabled|Boolean|true|Swap badly performing pairs for better performing ones| |SwapSignalRules|Array|[ "Swap" ]|Rules used to buy the replacement pair with| |SwapTimeout|Number|10800|How long to wait before making a swap (in seconds)| |ArbitrageEnabled|Boolean|false|Enable arbitrage for that pair| |ArbitrageMarkets|Array|[ "ETH" ]|Markets to use for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be used| |ArbitrageType|String|"Reverse"|Type of arbitrage to use. Available values: Direct, Reverse. When omitted both types will be used| |ArbitrageBuyMultiplier|Number|0.99|Percentage of the arbitrage pair to buy| |ArbitrageSellMultiplier|Number|0.99|Percentage of the arbitrage pair to sell| |ArgbitrageSignalRules|Array|[ "Arbitrage" ]|Rules used to arbitrage pairs| ###### Rule Conditions Rule is considered satisfied when all the conditions are met. When trailing is enabled, its conditions must be met first. You can have multiple sets of conditions within a single rule. Is is not necessary to specify a Signal if none of the conditions are signal-specific. |Setting|Type|Signal Specific|Description| |-|:-:|:-:|-| |Signal|String|N/A|Signal with which to calculate signal-specific conditions| |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| |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| |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+| |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| |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| |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| |MinPrice|Number|No|Minimum current price of the coin| |MaxPrice|Number|No|Maximum current price of the coin| |MinPriceChange|Number|Yes|The minimal rate of change of price withing specified period frame (percentage)| |MaxPriceChange|Number|Yes|The maximal rate of change of price withing specified period frame (percentage)| |MinSpread|Number|No|Minimum difference between current bid and ask price (percentage)| |MaxSpread|Number|No|Maximum difference between current bid and ask price (percentage)| |MinVolume|Number|Yes|Minimum coin volume within the specified signal's period. Do not expect 24h volume in this category| |MaxVolume|Number|Yes|Maximum coin volume within the specified signal's period. Do not expect 24h volume in this category| |MinVolumeChange|Number|Yes|The minimal rate of change of volume withing specified period frame (percentage)| |MaxVolumeChange|Number|Yes|The maximal rate of change of volume withing specified period frame (percentage)| |MinVolatility|Number|Yes|Minimum average volatility of a coin within its own specified timeframe| |MaxVolatility|Number|Yes|Maximum average volatility of a coin within its own specified timeframe| |MinAge|Number|No|Minimum trading pair's age (in days, e.g. 1.5 is 36 hours)| |MaxAge|Number|No|Maximum trading pair's age (in days, e.g. 1.5 is 36 hours)| |MinLastBuyAge|Number|No|Minimum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)| |MaxLastBuyAge|Number|No|Maximum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)| |MinMargin|Number|No|Minimum trading pair's margin| |MaxMargin|Number|No|Maximum trading pair's margin| |MinMarginChange|Number|No|Minimum trading pair's margin change since last buy| |MaxMarginChange|Number|No|Maximum trading pair's margin change since last buy| |MinAmount|Number|No|Minimum trading pair's total purchase amount| |MaxAmount|Number|No|Maximum trading pair's total purchase amount| |MinCost|Number|No|Minimum trading pair's total current cost| |MaxCost|Number|No|Maximum trading pair's total current cost| |MinDCALevel|Number|No|Minimum trading pair's DCA level| |MaxDCALevel|Number|No|Maximum trading pair's DCA level| |MinArbitrage|Number|No|Minimum triangular arbitrage value| |MaxArbitrage|Number|No|Maximum triangular arbitrage value| |ArbitrageMarket|String|No|Market to look for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be considered| |ArbitrageType|String|No|Type of arbitrage to look for. Available values: Direct, Reverse. When omitted both types will be considered| |Pairs|Array|No|List of pairs to directly apply the rule to| |NotPairs|Array|No|List of pairs to not apply the rule to| |SignalRules|Array|No|List of signal rules that were used to buy a pair| |NotSignalRules|Array|No|List of signal rules that were not used to buy a pair| ###### Rule Trailing Trailing is optional and is only supported by the Signal Rules. Trailing starts when all the Rule Trailing StartConditions are met. Trailing ends when all the conditions of the rule are met, at any point between MinDuration and MaxDuration. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable trailing| |MinDuration|Number|25|Minimum trail duration (in seconds)| |MaxDuration|Number|240|Maximum trail duration (in seconds)| |StartConditions|Array|[Rule Conditions](#Rule_Conditions)|Begin trailing when all the below conditions are met.| #### Notification Configuration Read more about how nofitications work in the [notifications](#Notifications) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|false|Enable / disable notifications| |TelegramEnabled|Boolean|true|Enabled / disable Telegram notifications| |TelegramBotToken|String|-|Your Telegram bot's token| |TelegramChatId|Number|-|Your Telegram chat id| |TelegramAlertsEnabled|Boolean|true|Enable phone alerts with Telegram messages| #### Backtesting Configuration Read more about how backtesting works in the [backtesting](#Backtesting) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|false|Enable / disable backtesting| |Replay|Boolean|false|Enabled / disable snapshots replay| |ReplayOutput|Boolean|true|Display replay output in the log| |ReplaySpeed|Number|500|Replay speed (1 = SnapshotInterval speed)| |ReplayStartIndex|Number|null|Snapshot index to start replay with| |ReplayEndIndex|Number|null|Snapshot index to end replay with| |DeleteLogs|Boolean|false|Delete all existing logs before running backtesting| |DeleteAccountData|Boolean|false|Delete account data before running backtesting| |CopyAccountDataPath|String|null|Path to copy existing account data file from for backtesting| |TradingSpeedEasing|Number|0|Slow down the trading service while replaying snapshots| |TradingRulesSpeedEasing|Number|0|Slow down the trading rules service while replaying snapshots| |SignalRulesSpeedEasing|Number|0|Slow down the signal rules service while replaying snapshots| |SnapshotsInterval|Number|1|How often to take snapshots (in seconds)| |SnapshotsPath|String|data/backtesting|Where to save the new snapshots or to load existing ones when replaying| #### Other Configuration ###### Exchange Exchange-specific configuration. Don't change the default values unless you know what you're doing. ###### Logging Logging configuration. Here you can disable logging completely, change the default log file paths, verbosity and format. ###### Paths Paths to all the configuration files. Do not change unless you know what you're doing. ###### Caching Currently not in use. ###### Integration Currently not in use. Trading -------------- There are two ways you can trade with IntelliTrader. Virtual trading is enabled on by default. DO NOT switch to live trading until you are fully confident with the bot and are familiar with the way it does trading. Conceptually 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. #### Virtual Trading Virtual trading is enabled by default. You don't need to provide your API Key to virtual trade. To enable virtual trading, set *VirtualTrading* to true in config/trading.json. You might want to change *VirtualAccountInitialBalance* to reflect your starting balance for testing. #### Live Trading To trade on an exchange, first you need to create an encrypted file that will hold your API keys. Open data/encrypt-keys and change public_key to your API key and private_key to your API secret, then run it. You 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. Important: 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. Also, to make the profit stats accurate, you need to set *AccountInitialBalance* to your current BTC/ETH balance (depending on the market you use). That's it, you are ready for exchange trading! #### Trailing When 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. Example trailing story for value -1 (percent): *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.* #### Signals Signals are used to buy new pairs based on the predefined rules. Read more about how signal rules work in the [signal rules](#Signal_Rules) section. #### Global Rating An average value of all the ratings combined (for current market & exchange) #### DCA Buy additional position at a lower price than the original purchase price. This brings the average price you've paid the pair down. #### Stop Loss Sell a pair at a specified negative margin to avoid it dipping even lower. #### Pair Swapping Swap badly performing pairs for better performing ones. You 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. #### Arbitrage *TODO: Add detailed explanation here* #### Backtesting Backtesting 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. Rules ------------- #### Signal Rules Signal Rules are used to buy new pairs based on the specific conditions. This can also optionally have trailing. If 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. If trailing isn't enabled it buys based only on the specific conditions set. Add "Action": "Swap" if you want the signal rule to only be used for pair swapping. Add "Action": "Arbitrage" if you want the signal rule to only be used for arbitrage. #### Trading Rules Trading 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. Available modifiers for Trading Rules are any trading.json setting that begins with Buy, Sell or DCALevels. This is also where you would setup your Swap Specific Rules. Web Interface ------------- To access the web interface, simply open http://localhost:7000 in your browser (or replace 7000 with the port you have configured). #### Status Bar Status 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. #### Dashboard This 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. Columns 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. You 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. The log button will display the last 5 lines from the log file. |Column|Description| |-|-| |More|Displayed on low resolution screens and when clicked shows additional data| |Pair #1|Pair's name| |Pair #2|Pair's name, including the current DCA level| |DCA|Current DCA level| |Margin|Current profit percentage. If you sold right now, this is what you would make, approximately| |Target|Target sell point in profit. Also known as the profit margin| |Rating|Pair's current rating based on the signals source, Green means it is currently higher than the purchase rating| |Rating Bought|Rating that the pair was purchased at| |Age|How long a pair has been held for| |Amount|Total amount of the pair you currently hold| |Cost|Current value of the pair| |Cost Bought|Purchase value of the pair| |Price|Current price of the pair on the exchange| |Price Bought|Price paid on purchase| |Spread|Difference between current bid and ask price| |Signal Rule|Signal rule used to buy the pair| |Trading Rules|Trading rules currently applied to the pair| |Order Dates|The dates of the purchase orders| |Order IDs|The order Ids on the exchange| Along 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. #### Market This 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. Columns 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. You 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. The log button will display the last 5 lines from the log file. |Column|Description| |-|-| |More|Displayed on low resolution screens and when clicked shows additional data| |Name|Pair's name| |Rating|Ratings for each signal| |% Rating Change|Percentage rating change for each signal| |Price|Current pair's price (same for every signal)| |% Price Change|Percentage price change for each signal| |Spread|Difference between current bid and ask price| |Arbitrage|Triangular arbitrage values for every available market| |Volume|Volume for each signal| |% Volume Change|Percentage volume change for each signal| |Volatility|Volatility for each signal| |Trading Rules|Currently applied trading rules| |Signal Rules|Currently applied signal rules| #### Stats Here you can see your total overall profit and current account balance. There is also a breakdown of profits by day, along with other information, like average margin. You can click on the number of trades to see information about the orders completed on that particular day. You can also open the Rules Analyzer page to check how each rule is performing. #### Settings Here you can temporarily enable or disable a small subset of bot options. If 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). There are also three options available on that page: - 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. - 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. - Log Out: log out of IntelliTrader #### Log Displays the last 500 lines from the log file. #### Help This page Notifications ------------- #### Telegram In 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. Talk to @botfather to create a new bot and then talk to @FalconGate_Bot to get your telegram chat id (type /get\_my\_id). Health Checks ------------- Health 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. Troubleshooting ------------- #### IntelliTrader is not starting or crashing on startup Please 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. #### IntelliTrader is not trading or is behaving weirdly in general Again, check the log file for any errors, it is very likely that there will be a clue there. License & Disclaimer ------------- By using, or simply downloading IntelliTrader (the Software), you understand and accept the following: Licensing The 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: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode A full copy of the license is also included with the download. Limitation Of Liability In 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: (i) your access to, or use of, or inability to access or use the Software; (ii) any content of any third party used with the Software; (iii) any content obtained from the Software; and (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. We do not refund losses. Disclaimer Your 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. No warranty of any kind is expressed or implied, that: a) the Software will function, be secure or operate on any nominated platform; b) any errors or defects will be corrected; c) the Software is free of viruses or other harmful components; or d) the results of using the Software will meet your requirements. If you do not agree with any of the above, please do not download or use the Software. ================================================ FILE: IntelliTrader.Web/Static/Help/navigation.md ================================================ ================================================ FILE: IntelliTrader.Web/Static/Scripts/Vendor/mdwiki.js ================================================ (function () { /** * Block-Level Grammar */ var block = { newline: /^\n+/, code: /^( {4}[^\n]+\n*)+/, fences: noop, hr: /^( *[-*_]){3,} *(?:\n+|$)/, heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, nptable: noop, lheading: /^([^\n]+)\n *(=|-){3,} *\n*/, blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/, list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/, def: /^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, table: noop, paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, text: /^[^\n]+/ }; block.bullet = /(?:[*+-]|\d+\.)/; block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; block.item = replace(block.item, 'gm') (/bull/g, block.bullet) (); block.list = replace(block.list) (/bull/g, block.bullet) ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/) (); block._tag = '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code' + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo' + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b'; block.html = replace(block.html) ('comment', //) ('closed', /<(tag)[\s\S]+?<\/\1>/) ('closing', /])*?>/) (/tag/g, block._tag) (); block.paragraph = replace(block.paragraph) ('hr', block.hr) ('heading', block.heading) ('lheading', block.lheading) ('blockquote', block.blockquote) ('tag', '<' + block._tag) ('def', block.def) (); /** * Normal Block Grammar */ block.normal = merge({}, block); /** * GFM Block Grammar */ block.gfm = merge({}, block.normal, { fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/, paragraph: /^/ }); block.gfm.paragraph = replace(block.paragraph) ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|') (); /** * GFM + Tables Block Grammar */ block.tables = merge({}, block.gfm, { nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/ }); /** * Block Lexer */ function Lexer(options) { this.tokens = []; this.tokens.links = {}; this.options = options || marked.defaults; this.rules = block.normal; if (this.options.gfm) { if (this.options.tables) { this.rules = block.tables; } else { this.rules = block.gfm; } } } /** * Expose Block Rules */ Lexer.rules = block; /** * Static Lex Method */ Lexer.lex = function (src, options) { var lexer = new Lexer(options); return lexer.lex(src); }; /** * Preprocessing */ Lexer.prototype.lex = function (src) { src = src .replace(/\r\n|\r/g, '\n') .replace(/\t/g, ' ') .replace(/\u00a0/g, ' ') .replace(/\u2424/g, '\n'); return this.token(src, true); }; /** * Lexing */ Lexer.prototype.token = function (src, top) { var src = src.replace(/^ +$/gm, '') , next , loose , cap , bull , b , item , space , i , l; while (src) { // newline if (cap = this.rules.newline.exec(src)) { src = src.substring(cap[0].length); if (cap[0].length > 1) { this.tokens.push({ type: 'space' }); } } // code if (cap = this.rules.code.exec(src)) { src = src.substring(cap[0].length); cap = cap[0].replace(/^ {4}/gm, ''); this.tokens.push({ type: 'code', text: !this.options.pedantic ? cap.replace(/\n+$/, '') : cap }); continue; } // fences (gfm) if (cap = this.rules.fences.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'code', lang: cap[2], text: cap[3] }); continue; } // heading if (cap = this.rules.heading.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'heading', depth: cap[1].length, text: cap[2] }); continue; } // table no leading pipe (gfm) if (top && (cap = this.rules.nptable.exec(src))) { src = src.substring(cap[0].length); item = { type: 'table', header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3].replace(/\n$/, '').split('\n') }; for (i = 0; i < item.align.length; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = 'right'; } else if (/^ *:-+: *$/.test(item.align[i])) { item.align[i] = 'center'; } else if (/^ *:-+ *$/.test(item.align[i])) { item.align[i] = 'left'; } else { item.align[i] = null; } } for (i = 0; i < item.cells.length; i++) { item.cells[i] = item.cells[i].split(/ *\| */); } this.tokens.push(item); continue; } // lheading if (cap = this.rules.lheading.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'heading', depth: cap[2] === '=' ? 1 : 2, text: cap[1] }); continue; } // hr if (cap = this.rules.hr.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'hr' }); continue; } // blockquote if (cap = this.rules.blockquote.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'blockquote_start' }); cap = cap[0].replace(/^ *> ?/gm, ''); // Pass `top` to keep the current // "toplevel" state. This is exactly // how markdown.pl works. this.token(cap, top); this.tokens.push({ type: 'blockquote_end' }); continue; } // list if (cap = this.rules.list.exec(src)) { src = src.substring(cap[0].length); bull = cap[2]; this.tokens.push({ type: 'list_start', ordered: bull.length > 1 }); // Get each top-level item. cap = cap[0].match(this.rules.item); next = false; l = cap.length; i = 0; for (; i < l; i++) { item = cap[i]; // Remove the list item's bullet // so it is seen as the next token. space = item.length; item = item.replace(/^ *([*+-]|\d+\.) +/, ''); // Outdent whatever the // list item contains. Hacky. if (~item.indexOf('\n ')) { space -= item.length; item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); } // Determine whether the next list item belongs here. // Backpedal if it does not belong in this list. if (this.options.smartLists && i !== l - 1) { b = block.bullet.exec(cap[i + 1])[0]; if (bull !== b && !(bull.length > 1 && b.length > 1)) { src = cap.slice(i + 1).join('\n') + src; i = l - 1; } } // Determine whether item is loose or not. // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ // for discount behavior. loose = next || /\n\n(?!\s*$)/.test(item); if (i !== l - 1) { next = item[item.length - 1] === '\n'; if (!loose) loose = next; } this.tokens.push({ type: loose ? 'loose_item_start' : 'list_item_start' }); // Recurse. this.token(item, false); this.tokens.push({ type: 'list_item_end' }); } this.tokens.push({ type: 'list_end' }); continue; } // html if (cap = this.rules.html.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: this.options.sanitize ? 'paragraph' : 'html', pre: cap[1] === 'pre' || cap[1] === 'script', text: cap[0] }); continue; } // def if (top && (cap = this.rules.def.exec(src))) { src = src.substring(cap[0].length); this.tokens.links[cap[1].toLowerCase()] = { href: cap[2], title: cap[3] }; continue; } // table (gfm) if (top && (cap = this.rules.table.exec(src))) { src = src.substring(cap[0].length); item = { type: 'table', header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') }; for (i = 0; i < item.align.length; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = 'right'; } else if (/^ *:-+: *$/.test(item.align[i])) { item.align[i] = 'center'; } else if (/^ *:-+ *$/.test(item.align[i])) { item.align[i] = 'left'; } else { item.align[i] = null; } } for (i = 0; i < item.cells.length; i++) { item.cells[i] = item.cells[i] .replace(/^ *\| *| *\| *$/g, '') .split(/ *\| */); } this.tokens.push(item); continue; } // top-level paragraph if (top && (cap = this.rules.paragraph.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: 'paragraph', text: cap[1][cap[1].length - 1] === '\n' ? cap[1].slice(0, -1) : cap[1] }); continue; } // text if (cap = this.rules.text.exec(src)) { // Top-level should never reach here. src = src.substring(cap[0].length); this.tokens.push({ type: 'text', text: cap[0] }); continue; } if (src) { throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); } } return this.tokens; }; /** * Inline-Level Grammar */ var inline = { escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, url: noop, tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, link: /^!?\[(inside)\]\(href\)/, reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, br: /^ {2,}\n(?!\s*$)/, del: noop, text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/; inline.link = replace(inline.link) ('inside', inline._inside) ('href', inline._href) (); inline.reflink = replace(inline.reflink) ('inside', inline._inside) (); /** * Normal Inline Grammar */ inline.normal = merge({}, inline); /** * Pedantic Inline Grammar */ inline.pedantic = merge({}, inline.normal, { strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/ }); /** * GFM Inline Grammar */ inline.gfm = merge({}, inline.normal, { escape: replace(inline.escape)('])', '~|])')(), url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/, del: /^~~(?=\S)([\s\S]*?\S)~~/, text: replace(inline.text) (']|', '~]|') ('|', '|https?://|') () }); /** * GFM + Line Breaks Inline Grammar */ inline.breaks = merge({}, inline.gfm, { br: replace(inline.br)('{2,}', '*')(), text: replace(inline.gfm.text)('{2,}', '*')() }); /** * Inline Lexer & Compiler */ function InlineLexer(links, options) { this.options = options || marked.defaults; this.links = links; this.rules = inline.normal; if (!this.links) { throw new Error('Tokens array requires a `links` property.'); } if (this.options.gfm) { if (this.options.breaks) { this.rules = inline.breaks; } else { this.rules = inline.gfm; } } else if (this.options.pedantic) { this.rules = inline.pedantic; } } /** * Expose Inline Rules */ InlineLexer.rules = inline; /** * Static Lexing/Compiling Method */ InlineLexer.output = function (src, links, options) { var inline = new InlineLexer(links, options); return inline.output(src); }; /** * Lexing/Compiling */ InlineLexer.prototype.output = function (src) { var out = '' , link , text , href , cap; while (src) { // escape if (cap = this.rules.escape.exec(src)) { src = src.substring(cap[0].length); out += cap[1]; continue; } // autolink if (cap = this.rules.autolink.exec(src)) { src = src.substring(cap[0].length); if (cap[2] === '@') { text = cap[1][6] === ':' ? this.mangle(cap[1].substring(7)) : this.mangle(cap[1]); href = this.mangle('mailto:') + text; } else { text = escape(cap[1]); href = text; } out += '' + text + ''; continue; } // url (gfm) if (cap = this.rules.url.exec(src)) { src = src.substring(cap[0].length); text = escape(cap[1]); href = text; out += '' + text + ''; continue; } // tag if (cap = this.rules.tag.exec(src)) { src = src.substring(cap[0].length); out += this.options.sanitize ? escape(cap[0]) : cap[0]; continue; } // link if (cap = this.rules.link.exec(src)) { src = src.substring(cap[0].length); out += this.outputLink(cap, { href: cap[2], title: cap[3] }); continue; } // reflink, nolink if ((cap = this.rules.reflink.exec(src)) || (cap = this.rules.nolink.exec(src))) { src = src.substring(cap[0].length); link = (cap[2] || cap[1]).replace(/\s+/g, ' '); link = this.links[link.toLowerCase()]; if (!link || !link.href) { out += cap[0][0]; src = cap[0].substring(1) + src; continue; } out += this.outputLink(cap, link); continue; } // strong if (cap = this.rules.strong.exec(src)) { src = src.substring(cap[0].length); out += '' + this.output(cap[2] || cap[1]) + ''; continue; } // em if (cap = this.rules.em.exec(src)) { src = src.substring(cap[0].length); out += '' + this.output(cap[2] || cap[1]) + ''; continue; } // code if (cap = this.rules.code.exec(src)) { src = src.substring(cap[0].length); out += '' + escape(cap[2], true) + ''; continue; } // br if (cap = this.rules.br.exec(src)) { src = src.substring(cap[0].length); out += '
'; continue; } // del (gfm) if (cap = this.rules.del.exec(src)) { src = src.substring(cap[0].length); out += '' + this.output(cap[1]) + ''; continue; } // text if (cap = this.rules.text.exec(src)) { src = src.substring(cap[0].length); out += escape(cap[0]); continue; } if (src) { throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); } } return out; }; /** * Compile Link */ InlineLexer.prototype.outputLink = function (cap, link) { if (cap[0][0] !== '!') { return '' + this.output(cap[1]) + ''; } else { return ''
                + escape(cap[1])
                + ''; } }; /** * Smartypants Transformations */ InlineLexer.prototype.smartypants = function (text) { if (!this.options.smartypants) return text; return text .replace(/--/g, '—') .replace(/'([^']*)'/g, '‘$1’') .replace(/"([^"]*)"/g, '“$1”') .replace(/\.{3}/g, '…'); }; /** * Mangle Links */ InlineLexer.prototype.mangle = function (text) { var out = '' , l = text.length , i = 0 , ch; for (; i < l; i++) { ch = text.charCodeAt(i); if (Math.random() > 0.5) { ch = 'x' + ch.toString(16); } out += '&#' + ch + ';'; } return out; }; /** * Parsing & Compiling */ function Parser(options) { this.tokens = []; this.token = null; this.options = options || marked.defaults; } /** * Static Parse Method */ Parser.parse = function (src, options) { var parser = new Parser(options); return parser.parse(src); }; /** * Parse Loop */ Parser.prototype.parse = function (src) { this.inline = new InlineLexer(src.links, this.options); this.tokens = src.reverse(); var out = ''; while (this.next()) { out += this.tok(); } return out; }; /** * Next Token */ Parser.prototype.next = function () { return this.token = this.tokens.pop(); }; /** * Preview Next Token */ Parser.prototype.peek = function () { return this.tokens[this.tokens.length - 1] || 0; }; /** * Parse Text Tokens */ Parser.prototype.parseText = function () { var body = this.token.text; while (this.peek().type === 'text') { body += '\n' + this.next().text; } return this.inline.output(body); }; /** * Parse Current Token */ Parser.prototype.tok = function () { switch (this.token.type) { case 'space': { return ''; } case 'hr': { return '
\n'; } case 'heading': { return '' + this.inline.output(this.token.text) + '\n'; } case 'code': { if (this.options.highlight) { var code = this.options.highlight(this.token.text, this.token.lang); if (code != null && code !== this.token.text) { this.token.escaped = true; this.token.text = code; } } if (!this.token.escaped) { this.token.text = escape(this.token.text, true); } return '
'
                    + this.token.text
                    + '
\n'; } case 'table': { var body = '' , heading , i , row , cell , j; // header body += '\n\n'; for (i = 0; i < this.token.header.length; i++) { heading = this.inline.output(this.token.header[i]); body += this.token.align[i] ? '' + heading + '\n' : '' + heading + '\n'; } body += '\n\n'; // body body += '\n' for (i = 0; i < this.token.cells.length; i++) { row = this.token.cells[i]; body += '\n'; for (j = 0; j < row.length; j++) { cell = this.inline.output(row[j]); body += this.token.align[j] ? '' + cell + '\n' : '' + cell + '\n'; } body += '\n'; } body += '\n'; return '\n' + body + '
\n'; } case 'blockquote_start': { var body = ''; while (this.next().type !== 'blockquote_end') { body += this.tok(); } return '
\n' + body + '
\n'; } case 'list_start': { var type = this.token.ordered ? 'ol' : 'ul' , body = ''; while (this.next().type !== 'list_end') { body += this.tok(); } return '<' + type + '>\n' + body + '\n'; } case 'list_item_start': { var body = ''; while (this.next().type !== 'list_item_end') { body += this.token.type === 'text' ? this.parseText() : this.tok(); } return '
  • ' + body + '
  • \n'; } case 'loose_item_start': { var body = ''; while (this.next().type !== 'list_item_end') { body += this.tok(); } return '
  • ' + body + '
  • \n'; } case 'html': { return !this.token.pre && !this.options.pedantic ? this.inline.output(this.token.text) : this.token.text; } case 'paragraph': { return '

    ' + this.inline.output(this.token.text) + '

    \n'; } case 'text': { return '

    ' + this.parseText() + '

    \n'; } } }; /** * Helpers */ function escape(html, encode) { return html .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function replace(regex, opt) { regex = regex.source; opt = opt || ''; return function self(name, val) { if (!name) return new RegExp(regex, opt); val = val.source || val; val = val.replace(/(^|[^\[])\^/g, '$1'); regex = regex.replace(name, val); return self; }; } function noop() { } noop.exec = noop; function merge(obj) { var i = 1 , target , key; for (; i < arguments.length; i++) { target = arguments[i]; for (key in target) { if (Object.prototype.hasOwnProperty.call(target, key)) { obj[key] = target[key]; } } } return obj; } /** * Marked */ function marked(src, opt, callback) { if (callback || typeof opt === 'function') { if (!callback) { callback = opt; opt = null; } if (opt) opt = merge({}, marked.defaults, opt); var tokens = Lexer.lex(tokens, opt) , highlight = opt.highlight , pending = 0 , l = tokens.length , i = 0; if (!highlight || highlight.length < 3) { return callback(null, Parser.parse(tokens, opt)); } var done = function () { delete opt.highlight; var out = Parser.parse(tokens, opt); opt.highlight = highlight; return callback(null, out); }; for (; i < l; i++) { (function (token) { if (token.type !== 'code') return; pending++; return highlight(token.text, token.lang, function (err, code) { if (code == null || code === token.text) { return --pending || done(); } token.text = code; token.escaped = true; --pending || done(); }); })(tokens[i]); } return; } try { if (opt) opt = merge({}, marked.defaults, opt); return Parser.parse(Lexer.lex(src, opt), opt); } catch (e) { e.message += '\nPlease report this to https://github.com/chjj/marked.'; if ((opt || marked.defaults).silent) { return '

    An error occured:

    '
                        + escape(e.message + '', true)
                        + '
    '; } throw e; } } /** * Options */ marked.options = marked.setOptions = function (opt) { merge(marked.defaults, opt); return marked; }; marked.defaults = { gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartLists: false, silent: false, highlight: null, langPrefix: 'lang-' }; /** * Expose */ marked.Parser = Parser; marked.parser = Parser.parse; marked.Lexer = Lexer; marked.lexer = Lexer.lex; marked.InlineLexer = InlineLexer; marked.inlineLexer = InlineLexer.output; marked.parse = marked; if (typeof exports === 'object') { module.exports = marked; } else if (typeof define === 'function' && define.amd) { define(function () { return marked; }); } else { this.marked = marked; } }).call(function () { return this || (typeof window !== 'undefined' ? window : global); }()); (function ($) { 'use strict'; // hide the whole page so we dont see the DOM flickering // will be shown upon page load complete or error $('html').addClass('md-hidden-load'); // register our $.md object $.md = function (method) { if ($.md.publicMethods[method]) { return $.md.publicMethods[method].apply(this, Array.prototype.slice.call(arguments, 1) ); } else { $.error('Method ' + method + ' does not exist on jquery.md'); } }; // default config $.md.config = { title: null, useSideMenu: true, lineBreaks: 'gfm', additionalFooterText: '', anchorCharacter: '¶', tocAnchor: '[ ↑ ]' }; if (!$.mdContentRoot) { $.mdContentRoot = ''; } $.md.gimmicks = []; $.md.stages = []; // the location of the main markdown file we display $.md.mainHref = ''; // the in-page anchor that is specified after the ! $.md.inPageAnchor = ''; $.md.loglevel = { TRACE: 10, DEBUG: 20, INFO: 30, WARN: 40, ERROR: 50, FATAL: 60 }; // $.md.logThreshold = $.md.loglevel.DEBUG; $.md.logThreshold = $.md.loglevel.WARN; }(jQuery)); (function ($) { 'use strict'; $.md.getLogger = function () { var loglevel = $.md.loglevel; var log = function (logtarget) { var self = this; var level = loglevel[logtarget]; return function (msg) { if ($.md.logThreshold <= level) { console.log('[' + logtarget + '] ' + msg); } }; }; var logger = {}; logger.trace = log('TRACE'); logger.debug = log('DEBUG'); logger.info = log('INFO'); logger.warn = log('WARN'); logger.error = log('ERROR'); logger.fatal = log('FATAL'); return logger; }; }(jQuery)); (function ($) { 'use strict'; var log = $.md.getLogger(); $.Stage = function (name) { var self = $.extend($.Deferred(), {}); self.name = name; self.events = []; self.started = false; self.reset = function () { self.complete = $.Deferred(); self.outstanding = []; }; self.reset(); self.subscribe = function (fn) { if (self.started) { $.error('Subscribing to stage which already started!'); } self.events.push(fn); }; self.unsubscribe = function (fn) { self.events.remove(fn); }; self.executeSubscribedFn = function (fn) { var d = $.Deferred(); self.outstanding.push(d); // display an error if our done() callback is not called $.md.util.wait(2500).done(function () { if (d.state() !== 'resolved') { log.fatal('Timeout reached for done callback in stage: ' + self.name + '. Did you forget a done() call in a .subscribe() ?'); log.fatal('stage ' + name + ' failed running subscribed function: ' + fn); } }); var done = function () { d.resolve(); }; fn(done); }; self.run = function () { self.started = true; $(self.events).each(function (i, fn) { self.executeSubscribedFn(fn); }); // if no events are in our queue, we resolve immediately if (self.outstanding.length === 0) { self.resolve(); } // we resolve when all our registered events have completed $.when.apply($, self.outstanding) .done(function () { self.resolve(); }) .fail(function () { self.resolve(); }); }; self.done(function () { log.debug('stage ' + self.name + ' completed successfully.'); }); self.fail(function () { log.debug('stage ' + self.name + ' completed with errors!'); }); return self; }; }(jQuery)); (function ($) { 'use strict'; var log = $.md.getLogger(); function init() { $.md.stages = [ $.Stage('init'), // loads config, initial markdown and navigation $.Stage('load'), // will transform the markdown to html $.Stage('transform'), // HTML transformation finished $.Stage('ready'), // after we have a polished html skeleton $.Stage('skel_ready'), // will bootstrapify the skeleton $.Stage('bootstrap'), // before we run any gimmicks $.Stage('pregimmick'), // after we have bootstrapified the skeleton $.Stage('gimmick'), // postprocess $.Stage('postgimmick'), $.Stage('all_ready'), // used for integration tests, not intended to use in MDwiki itself $.Stage('final_tests') ]; $.md.stage = function (name) { var m = $.grep($.md.stages, function (e, i) { return e.name === name; }); if (m.length === 0) { $.error('A stage by name ' + name + ' does not exist'); } else { return m[0]; } }; } init(); function resetStages() { var old_stages = $.md.stages; $.md.stages = []; $(old_stages).each(function (i, e) { $.md.stages.push($.Stage(e.name)); }); } var publicMethods = {}; $.md.publicMethods = $.extend({}, $.md.publicMethods, publicMethods); function transformMarkdown(markdown) { var options = { gfm: true, tables: true, breaks: true }; if ($.md.config.lineBreaks === 'original') options.breaks = false; else if ($.md.config.lineBreaks === 'gfm') options.breaks = true; marked.setOptions(options); // get sample markdown var uglyHtml = marked(markdown); return uglyHtml; } function registerFetchMarkdown() { var md = ''; $.md.stage('init').subscribe(function (done) { var ajaxReq = { url: $.mdContentRoot + $.md.mainHref, dataType: 'text' }; $.ajax(ajaxReq).done(function (data) { // TODO do this elsewhere md = data; done(); }).fail(function () { var log = $.md.getLogger(); log.fatal('Could not get ' + $.md.mainHref); done(); }); }); // find baseUrl $.md.stage('transform').subscribe(function (done) { var len = $.md.mainHref.lastIndexOf('/'); var baseUrl = $.md.mainHref.substring(0, len + 1); $.md.baseUrl = baseUrl; done(); }); $.md.stage('transform').subscribe(function (done) { var uglyHtml = transformMarkdown(md); $('#md-content').html(uglyHtml); md = ''; var dfd = $.Deferred(); loadExternalIncludes(dfd); dfd.always(function () { done(); }); }); } // load [include](/foo/bar.md) external links function loadExternalIncludes(parent_dfd) { function findExternalIncludes() { return $('a').filter(function () { var href = $(this).attr('href'); var text = $(this).toptext(); var isMarkdown = $.md.util.hasMarkdownFileExtension(href); var isInclude = text === 'include'; var isPreview = text.startsWith('preview:'); return (isInclude || isPreview) && isMarkdown; }); } function selectPreviewElements($jqcol, num_elements) { function isTextNode(node) { return node.nodeType === 3; } var count = 0; var elements = []; $jqcol.each(function (i, e) { if (count < num_elements) { elements.push(e); if (!isTextNode(e)) count++; } }); return $(elements); } var external_links = findExternalIncludes(); // continue execution when all external resources are fully loaded var latch = $.md.util.countDownLatch(external_links.length); latch.always(function () { parent_dfd.resolve(); }); external_links.each(function (i, e) { var $el = $(e); var href = $el.attr('href'); var text = $el.toptext(); $.ajax({ url: $.mdContentRoot + href, dataType: 'text' }) .done(function (data) { var $html = $(transformMarkdown(data)); if (text.startsWith('preview:')) { // only insert the selected number of paragraphs; default 3 var num_preview_elements = parseInt(text.substring(8), 10) || 3; var $preview = selectPreviewElements($html, num_preview_elements); $preview.last().append(' ...read more ➜'); $preview.insertBefore($el.parent('p').eq(0)); $el.remove(); } else { $html.insertAfter($el.parents('p')); $el.remove(); } }).always(function () { latch.countDown(); }); }); } function isSpecialLink(href) { if (!href) return false; if (href.lastIndexOf('data:') >= 0) return true; if (href.startsWith('mailto:')) return true; if (href.startsWith('file:')) return true; if (href.startsWith('ftp:')) return true; // TODO capture more special links: every non-http link with : like // torrent:// etc. } // modify internal links so we load them through our engine function processPageLinks(domElement, baseUrl) { var html = $(domElement); if (baseUrl === undefined) { baseUrl = ''; } // HACK against marked: empty links will have empy href attribute // we remove the href attribute from the a tag html.find('a').not('#md-menu a').filter(function () { var $this = $(this); var attr = $this.attr('href'); if (!attr || attr.length === 0) $this.removeAttr('href'); }); html.find('a, img').each(function (i, e) { var link = $(e); // link must be jquery collection var isImage = false; var hrefAttribute = 'href'; if (!link.attr(hrefAttribute)) { isImage = true; hrefAttribute = 'src'; } var href = link.attr(hrefAttribute); if (href && href.lastIndexOf('#!') >= 0) return; if (isSpecialLink(href)) return; if (!isImage && href.startsWith('#') && !href.startsWith('#!')) { // in-page link link.click(function (ev) { ev.preventDefault(); $.md.scrollToInPageAnchor(href); }); } if (!$.md.util.isRelativeUrl(href)) return; if (isImage && !$.md.util.isRelativePath(href)) return; if (!isImage && $.md.util.isGimmickLink(link)) return; function build_link(url) { if ($.md.util.hasMarkdownFileExtension(url)) return '#!' + url; else return url; } var newHref = baseUrl + href; if (isImage) link.attr(hrefAttribute, newHref); else if ($.md.util.isRelativePath(href)) link.attr(hrefAttribute, build_link(newHref)); else link.attr(hrefAttribute, build_link(href)); }); } var navMD = ''; $.md.NavigationDfd = $.Deferred(); var ajaxReq = { url: $.mdContentRoot + 'navigation.md', dataType: 'text' }; $.ajax(ajaxReq).done(function (data) { navMD = data; $.md.NavigationDfd.resolve(); }).fail(function () { $.md.NavigationDfd.reject(); }); function registerBuildNavigation() { $.md.stage('init').subscribe(function (done) { $.md.NavigationDfd.done(function () { done(); }) .fail(function () { done(); }); }); $.md.stage('transform').subscribe(function (done) { if (navMD === '') { var log = $.md.getLogger(); log.info('no navgiation.md found, not using a navbar'); done(); return; } var navHtml = marked(navMD); // TODO why are }
    More Pair Pair DCA Margin Target Rating Rating Bought Age Amount Cost Cost Bought Price Price Bought Spread Signal Rule Trading Rules Order Dates Order Ids
    Total Total Avg Avg Avg Avg Total Total
    ================================================ FILE: IntelliTrader.Web/Views/Home/Help.cshtml ================================================ @model HelpViewModel @{ ViewData["Title"] = "Help"; ViewData["Instance"] = Model.InstanceName; ViewData["CustomPageHeader"] = true; } @section AddToHead{ }
    ================================================ FILE: IntelliTrader.Web/Views/Home/Log.cshtml ================================================ @model LogViewModel @{ ViewData["Title"] = "Log"; ViewData["Instance"] = Model.InstanceName; }

    @foreach (var entry in Model.LogEntries) {
    @Html.Raw(entry)
    }

    ================================================ FILE: IntelliTrader.Web/Views/Home/Login.cshtml ================================================ @model LoginViewModel @{ ViewData["Title"] = "Login"; }
    @Html.PasswordFor(m => m.Password, new { @class = "form-control" })

    @Html.AntiForgeryToken()
    ================================================ FILE: IntelliTrader.Web/Views/Home/Market.cshtml ================================================ @model MarketViewModel @{ ViewData["Title"] = "Market"; ViewData["Instance"] = Model.InstanceName; } @section AddToHead{ }
    More Name Rating % Rating Change Price % Price Change Spread Arbitrage Volume % Volume Change Volatility Trading Rules Signal Rules
    ================================================ FILE: IntelliTrader.Web/Views/Home/Rules.cshtml ================================================ @model RulesViewModel @{ ViewData["Title"] = "Rules Analyzer"; ViewData["Instance"] = Model.InstanceName; } @section AddToHead{ }
    Signal Rules: @Model.SignalRuleStats.Count
    @foreach (var trade in Model.SignalRuleStats) { }
    Signal Rule Profit Fees Cost Trades Orders Swaps Avg. Age Avg. Margin Avg. Margin DCA Avg. DCA
    @trade.Key @trade.Value.TotalProfit.ToString("0.00000000") @trade.Value.TotalFees.ToString("0.00000000") @trade.Value.TotalCost.ToString("0.00000000") @trade.Value.TotalTrades @trade.Value.TotalOrders @trade.Value.TotalSwaps @trade.Value.Age.DefaultIfEmpty(0).Average().ToString("0.00") @trade.Value.Margin.DefaultIfEmpty(0).Average().ToString("0.00") @trade.Value.MarginDCA.DefaultIfEmpty(0).Average().ToString("0.00") @trade.Value.DCA.DefaultIfEmpty(0).Average().ToString("0.00")
    ================================================ FILE: IntelliTrader.Web/Views/Home/Settings.cshtml ================================================ @model SettingsViewModel @{ ViewData["Title"] = "Settings"; ViewData["Instance"] = Model.InstanceName; ViewData["CustomPageHeader"] = true; } @section AddToHead{ }
    @using (Html.BeginForm()) {
    @Html.CheckBoxFor(model => model.BuyEnabled)@Html.LabelFor(model => model.BuyEnabled)
    @Html.CheckBoxFor(model => model.BuyDCAEnabled)@Html.LabelFor(model => model.BuyDCAEnabled)
    @Html.CheckBoxFor(model => model.SellEnabled)@Html.LabelFor(model => model.SellEnabled)
    @Html.CheckBoxFor(model => model.HealthCheckEnabled)@Html.LabelFor(model => model.HealthCheckEnabled)
    @Html.CheckBoxFor(model => model.TradingSuspended)@Html.LabelFor(model => model.TradingSuspended)
    }

    Advanced

    @foreach (var kvp in Model.Configs) {
    @Html.Raw(kvp.Value)
    }
    ================================================ FILE: IntelliTrader.Web/Views/Home/Stats.cshtml ================================================ @model StatsViewModel @{ ViewData["Title"] = "Stats"; ViewData["Instance"] = Model.InstanceName; } @section AddToHead{ }
    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
    @foreach (var kvp in Model.Trades.OrderByDescending(t => t.Key).Take(30)) { }
    Date Trades % Profit Profit Fees Avg. Margin Avg. Margin DCA Avg. Rating Bought Avg. Rating Sold Avg. Gl. Rating Bought Avg. Gl. Rating Sold
    @kvp.Key.ToString("yyyy-MM-dd") @Html.ActionLink(kvp.Value.Count.ToString(), "Trades", "Home", new { id = kvp.Key.ToString("o") }, new { @class = "btn btb-sm btn-success trades-link" }) @{ var percentage = kvp.Value.Where(t => !t.IsSwap).Sum(t => t.Profit) / Model.Balances[kvp.Key] * 100; @Html.Raw(percentage.ToString("0.00")); } @kvp.Value.Where(t => !t.IsSwap).Sum(t => t.Profit).ToString("0.00000000") @kvp.Value.Sum(t => t.FeesTotal).ToString("0.00000000") @{ var trades = kvp.Value.Where(t => !t.IsSwap && t.OrderDates != null && t.OrderDates.Count == 1); if (trades.Count() > 0) { var avgMargin = trades.Average(t => t.Profit / (t.Cost + (t.Metadata?.AdditionalCosts ?? 0)) * 100); @Html.Raw(avgMargin.ToString("0.00")); } else { @Html.Raw("N/A"); } } @{ var tradesDCA = kvp.Value.Where(t => !t.IsSwap && t.OrderDates != null && t.OrderDates.Count > 1); if (tradesDCA.Count() > 0) { var avgMargin = tradesDCA.Average(t => t.Profit / (t.Cost + (t.Metadata?.AdditionalCosts ?? 0)) * 100); @Html.Raw(avgMargin.ToString("0.00")); } else { @Html.Raw("N/A"); } } @{ var tradesRatingBought = kvp.Value.Where(t => t.Metadata?.BoughtRating != null); if (tradesRatingBought.Count() > 0) { var avgRatingBought = tradesRatingBought.Average(t => t.Metadata.BoughtRating.Value); @Html.Raw(avgRatingBought.ToString("0.000")); } else { @Html.Raw("N/A"); } } @{ var tradesRatingSold = kvp.Value.Where(t => t.Metadata?.CurrentRating != null); if (tradesRatingSold.Count() > 0) { var avgRatingSold = tradesRatingSold.Average(t => t.Metadata.CurrentRating.Value); @Html.Raw(avgRatingSold.ToString("0.000")); } else { @Html.Raw("N/A"); } } @{ var tradesGlobalRatingBought = kvp.Value.Where(t => t.Metadata?.BoughtGlobalRating != null); if (tradesGlobalRatingBought.Count() > 0) { var avgGlobalRatingBought = tradesGlobalRatingBought.Average(t => t.Metadata.BoughtGlobalRating.Value); @Html.Raw(avgGlobalRatingBought.ToString("0.000")); } else { @Html.Raw("N/A"); } } @{ var tradesGlobalRatingSold = kvp.Value.Where(t => t.Metadata?.CurrentGlobalRating != null); if (tradesGlobalRatingSold.Count() > 0) { var avgGlobalRatingSold = tradesGlobalRatingSold.Average(t => t.Metadata.CurrentGlobalRating.Value); @Html.Raw(avgGlobalRatingSold.ToString("0.000")); } else { @Html.Raw("N/A"); } }

    ================================================ FILE: IntelliTrader.Web/Views/Home/Trades.cshtml ================================================ @model TradesViewModel @{ ViewData["Title"] = "Trades"; ViewData["Instance"] = Model.InstanceName; } @section AddToHead{ }
    Date: @Model.Date.ToOffset(TimeSpan.FromHours(Model.TimezoneOffset)).ToString("yyyy-MM-dd"), Total: @Model.Trades.Count
    @foreach (var trade in Model.Trades.OrderByDescending(t => t.SellDate)) { }
    Date Pair DCA Level Margin Profit Fees Age Rating Bought Rating Sold Gl. Rating Bought Gl. Rating Sold Signal Rule Swap Pair Arbitrage % Arbitrage
    @trade.SellDate.ToOffset(TimeSpan.FromHours(Model.TimezoneOffset)).ToString("yyyy-MM-dd HH:mm:ss") @if (trade.Metadata?.OriginalPair != null) { @Html.Raw(trade.Metadata.OriginalPair); } else { @Html.Raw(trade.Pair); } @if (trade.Metadata?.OriginalPair == null) { @Html.Raw((trade.OrderDates.Count - 1) + (trade.Metadata?.AdditionalDCALevels ?? 0)) } else { 0 } @Html.Raw((trade.Profit / (trade.Cost + (trade.Metadata?.AdditionalCosts ?? 0)) * 100).ToString("0.00")) @trade.Profit.ToString("0.00000000") @trade.FeesTotal.ToString("0.00000000") @if (trade.OrderDates != null && trade.OrderDates.Count > 0) { @Html.Raw((trade.SellDate - trade.OrderDates.Min()).TotalDays.ToString("0.00")); } else { N/A } @if (trade.Metadata?.BoughtRating != null) { @Html.Raw(trade.Metadata.BoughtRating.Value.ToString("0.000")); } else { N/A } @if (trade.Metadata?.CurrentRating != null) { @Html.Raw(trade.Metadata.CurrentRating.Value.ToString("0.000")); } else { N/A } @if (trade.Metadata?.BoughtGlobalRating != null) { @Html.Raw(trade.Metadata.BoughtGlobalRating.Value.ToString("0.000")); } else { N/A } @if (trade.Metadata?.CurrentGlobalRating != null) { @Html.Raw(trade.Metadata.CurrentGlobalRating.Value.ToString("0.000")); } else { N/A } @if (trade.Metadata?.SignalRule != null) { @Html.Raw(trade.Metadata.SignalRule); } else { N/A } @if (trade.Metadata?.SwapPair != null && trade.IsSwap) { @Html.Raw(trade.Metadata.SwapPair); } else { N/A } @if (trade.Metadata?.Arbitrage != null && trade.IsArbitrage) { @Html.Raw(trade.Metadata.Arbitrage); } else { N/A } @if (trade.Metadata?.ArbitragePercentage != null && trade.IsArbitrage) { @Html.Raw(trade.Metadata.ArbitragePercentage.GetValueOrDefault().ToString("0.00")); } else { N/A }
    ================================================ FILE: IntelliTrader.Web/Views/Shared/_Layout.cshtml ================================================  @if (User.Identity.IsAuthenticated) { @ViewData["Title"] - IntelliTrader (@ViewData["Instance"]) } else { @ViewData["Title"] - IntelliTrader } @if (IsSectionDefined("AddToHead")) { @RenderSection("AddToHead", required: false) }
    @if (!ViewData.ContainsKey("CustomPageHeader")) { }
    @RenderBody()
    ================================================ FILE: IntelliTrader.Web/Views/_ViewImports.cshtml ================================================ @using IntelliTrader.Web @using IntelliTrader.Web.Models @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: IntelliTrader.Web/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: IntelliTrader.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader", "IntelliTrader\IntelliTrader.csproj", "{E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Core", "IntelliTrader.Core\IntelliTrader.Core.csproj", "{E4702F59-BE3D-41E9-9769-B49122AE37F3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Signals", "Signals", "{E9A5196B-B397-4AC8-90F1-7DCFB887E4DF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Signals.Base", "IntelliTrader.Signals.Base\IntelliTrader.Signals.Base.csproj", "{60F0F4CC-9480-4C6E-B753-6754E47A4EE3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Signals.TradingView", "IntelliTrader.Signals.TradingView\IntelliTrader.Signals.TradingView.csproj", "{7587243F-1361-4749-85EA-457303C48DC5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exchange", "Exchange", "{7C3B0DA6-D479-4B65-994E-4277DF073B83}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Exchange.Base", "IntelliTrader.Exchange.Base\IntelliTrader.Exchange.Base.csproj", "{55A29746-4B68-42B6-BC07-5243016BDA17}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Exchange.Binance", "IntelliTrader.Exchange.Binance\IntelliTrader.Exchange.Binance.csproj", "{A7487CA9-F3F6-496E-9D58-11D893BD8813}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Trading", "IntelliTrader.Trading\IntelliTrader.Trading.csproj", "{F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Submodules", "Submodules", "{3509C1FF-281E-4334-8D84-D161488A3DC6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeSharp", "Submodules\ExchangeSharp\ExchangeSharp\ExchangeSharp.csproj", "{6569167D-5CD5-488D-9492-43210C0AEC0D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Web", "IntelliTrader.Web\IntelliTrader.Web.csproj", "{802FF51C-E66D-44BC-B26F-C72C68ED9E56}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Rules", "IntelliTrader.Rules\IntelliTrader.Rules.csproj", "{A7177BC2-2912-440A-BBBE-A2DE3D001918}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTrader.Backtesting", "IntelliTrader.Backtesting\IntelliTrader.Backtesting.csproj", "{20657014-0DC0-43E6-8351-E94F78095DFC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{33CCA636-C1F7-419E-AF89-B771E5CEC0BD}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore .gitmodules = .gitmodules Disclaimer.txt = Disclaimer.txt License.txt = License.txt Publish.bat = Publish.bat Publish.sh = Publish.sh README.md = README.md EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Debug|Any CPU.Build.0 = Debug|Any CPU {E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Release|Any CPU.ActiveCfg = Release|Any CPU {E29CD1E6-AA7C-4BF8-85F3-CB3813CC7B60}.Release|Any CPU.Build.0 = Release|Any CPU {E4702F59-BE3D-41E9-9769-B49122AE37F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E4702F59-BE3D-41E9-9769-B49122AE37F3}.Debug|Any CPU.Build.0 = Debug|Any CPU {E4702F59-BE3D-41E9-9769-B49122AE37F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {E4702F59-BE3D-41E9-9769-B49122AE37F3}.Release|Any CPU.Build.0 = Release|Any CPU {60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Debug|Any CPU.Build.0 = Debug|Any CPU {60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Release|Any CPU.ActiveCfg = Release|Any CPU {60F0F4CC-9480-4C6E-B753-6754E47A4EE3}.Release|Any CPU.Build.0 = Release|Any CPU {7587243F-1361-4749-85EA-457303C48DC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7587243F-1361-4749-85EA-457303C48DC5}.Debug|Any CPU.Build.0 = Debug|Any CPU {7587243F-1361-4749-85EA-457303C48DC5}.Release|Any CPU.ActiveCfg = Release|Any CPU {7587243F-1361-4749-85EA-457303C48DC5}.Release|Any CPU.Build.0 = Release|Any CPU {55A29746-4B68-42B6-BC07-5243016BDA17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {55A29746-4B68-42B6-BC07-5243016BDA17}.Debug|Any CPU.Build.0 = Debug|Any CPU {55A29746-4B68-42B6-BC07-5243016BDA17}.Release|Any CPU.ActiveCfg = Release|Any CPU {55A29746-4B68-42B6-BC07-5243016BDA17}.Release|Any CPU.Build.0 = Release|Any CPU {A7487CA9-F3F6-496E-9D58-11D893BD8813}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7487CA9-F3F6-496E-9D58-11D893BD8813}.Debug|Any CPU.Build.0 = Debug|Any CPU {A7487CA9-F3F6-496E-9D58-11D893BD8813}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7487CA9-F3F6-496E-9D58-11D893BD8813}.Release|Any CPU.Build.0 = Release|Any CPU {F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {F0ED2F66-4A02-4B9B-9775-0FCEEDC0E5C2}.Release|Any CPU.Build.0 = Release|Any CPU {6569167D-5CD5-488D-9492-43210C0AEC0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6569167D-5CD5-488D-9492-43210C0AEC0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {6569167D-5CD5-488D-9492-43210C0AEC0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {6569167D-5CD5-488D-9492-43210C0AEC0D}.Release|Any CPU.Build.0 = Release|Any CPU {802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Debug|Any CPU.Build.0 = Debug|Any CPU {802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Release|Any CPU.ActiveCfg = Release|Any CPU {802FF51C-E66D-44BC-B26F-C72C68ED9E56}.Release|Any CPU.Build.0 = Release|Any CPU {A7177BC2-2912-440A-BBBE-A2DE3D001918}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7177BC2-2912-440A-BBBE-A2DE3D001918}.Debug|Any CPU.Build.0 = Debug|Any CPU {A7177BC2-2912-440A-BBBE-A2DE3D001918}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7177BC2-2912-440A-BBBE-A2DE3D001918}.Release|Any CPU.Build.0 = Release|Any CPU {20657014-0DC0-43E6-8351-E94F78095DFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {20657014-0DC0-43E6-8351-E94F78095DFC}.Debug|Any CPU.Build.0 = Debug|Any CPU {20657014-0DC0-43E6-8351-E94F78095DFC}.Release|Any CPU.ActiveCfg = Release|Any CPU {20657014-0DC0-43E6-8351-E94F78095DFC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {60F0F4CC-9480-4C6E-B753-6754E47A4EE3} = {E9A5196B-B397-4AC8-90F1-7DCFB887E4DF} {7587243F-1361-4749-85EA-457303C48DC5} = {E9A5196B-B397-4AC8-90F1-7DCFB887E4DF} {55A29746-4B68-42B6-BC07-5243016BDA17} = {7C3B0DA6-D479-4B65-994E-4277DF073B83} {A7487CA9-F3F6-496E-9D58-11D893BD8813} = {7C3B0DA6-D479-4B65-994E-4277DF073B83} {6569167D-5CD5-488D-9492-43210C0AEC0D} = {3509C1FF-281E-4334-8D84-D161488A3DC6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {524E84EB-C228-46B9-B474-621BC86BB3E5} EndGlobalSection EndGlobal ================================================ FILE: License.txt ================================================ Attribution-NonCommercial-ShareAlike 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. k. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. l. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. m. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. n. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and b. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. b. ShareAlike. In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ================================================ FILE: Publish/Disclaimer.txt ================================================ By using, or simply downloading IntelliTrader (the Software), you understand and accept the following: Licensing The 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: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode A full copy of the license is also included with the download. Limitation Of Liability In 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: (i) your access to, or use of, or inability to access or use the Software; (ii) any content of any third party used with the Software; (iii) any content obtained from the Software; and (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. We do not refund losses. Disclaimer Your 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. No warranty of any kind is expressed or implied, that: a) the Software will function, be secure or operate on any nominated platform; b) any errors or defects will be corrected; c) the Software is free of viruses or other harmful components; or d) the results of using the Software will meet your requirements. If you do not agree with any of the above, please do not download or use the Software. ================================================ FILE: Publish/Help.url ================================================ [InternetShortcut] URL=https://github.com/jazzonaut/IntelliTrader/wiki IconFile=https://assets-cdn.github.com/favicon.ico IconIndex=1 ================================================ FILE: Publish/IntelliTrader.sh ================================================ #!/bin/bash dotnet bin/IntelliTrader.dll ================================================ FILE: Publish/License.txt ================================================ Attribution-NonCommercial-ShareAlike 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. k. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. l. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. m. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. n. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and b. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. b. ShareAlike. In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ================================================ FILE: Publish/config/backtesting.json ================================================ { "Backtesting": { "Enabled": false, "Replay": false, "ReplayOutput": true, "ReplaySpeed": 50, "ReplayStartIndex": null, "ReplayEndIndex": null, "DeleteLogs": true, "DeleteAccountData": true, "CopyAccountDataPath": null, "TradingSpeedEasing": 0, "TradingRulesSpeedEasing": 0, "SignalRulesSpeedEasing": 0, "SnapshotsInterval": 1, "SnapshotsPath": "data/backtesting" } } ================================================ FILE: Publish/config/core.json ================================================ { "Core": { "DebugMode": false, "PasswordProtected": true, "Password": "b84967c4f073b71405404f3719c788cd", "InstanceName": "Main", "TimezoneOffset": 1, "HealthCheckEnabled": true, "HealthCheckInterval": 180, "HealthCheckSuspendTradingTimeout": 900, "HealthCheckFailuresToRestartServices": 5 } } ================================================ FILE: Publish/config/exchange.json ================================================ { "Exchange": { "KeysPath": "data/keys.bin", "RateLimitOccurences": 40, "RateLimitTimeframe": 10 } } ================================================ FILE: Publish/config/logging.json ================================================ { "Logging": { "Enabled": true, "MinimumLevel": { "Default": "Verbose", "Override": { "System": "Warning", "Microsoft": "Warning" } }, "WriteTo": [ { "Name": "Logger", "Args": { "configureLogger": { "WriteTo": [ { "Name": "Console", "Args": { "outputTemplate": "{Timestamp:HH:mm:ss} [{Level:u3}] {Message}{NewLine}{Exception}", "theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Literate, Serilog.Sinks.Console", "restrictedToMinimumLevel": "Information" } }, { "Name": "RollingFile", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] {Message}{NewLine}{Exception}", "pathFormat": "log/{Date}-general.txt", "retainedFileCountLimit": 1000 } } ], "Filter": [ { "Name": "ByIncludingOnly", "Args": { "expression": "Trade is null" } } ] } } }, { "Name": "Logger", "Args": { "configureLogger": { "WriteTo": [ { "Name": "RollingFile", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] {Message}{NewLine}", "pathFormat": "log/{Date}-trades.txt", "retainedFileCountLimit": 1000 } } ], "Filter": [ { "Name": "ByIncludingOnly", "Args": { "expression": "Trade is not null" } } ] } } } ] } } ================================================ FILE: Publish/config/notification.json ================================================ { "Notification": { "Enabled": false, "TelegramEnabled": true, "TelegramBotToken": "", "TelegramChatId": 0, "TelegramAlertsEnabled": true } } ================================================ FILE: Publish/config/paths.json ================================================ { "Paths": { "Core": "core.json", "Logging": "logging.json", "Trading": "trading.json", "Exchange": "exchange.json", "Signals": "signals.json", "Rules": "rules.json", "Notification": "notification.json", "Web": "web.json", "Backtesting": "backtesting.json" } } ================================================ FILE: Publish/config/rules.json ================================================ { "Rules": { "Modules": [ { "Module": "Signals", "Configuration": { "ProcessingMode": "AllMatches", "CheckInterval": 0.1 }, "Entries": [ { "Enabled": false, "Name": "Buy-Arbitrage", "Action": "Arbitrage", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "MinArbitrage": 4 } ] }, { "Enabled": true, "Name": "Buy-Safe", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinVolatility": 2.5, "MaxVolatility": 10, "MaxPriceChange": 5 }, { "Signal": "TV-15m", "MinRating": 0.30, "MaxPriceChange": 5 }, { "Signal": "TV-1h", "MinRating": 0.25, "MinVolume": 100000, "MaxPriceChange": 6 }, { "Signal": "TV-4h", "MinRating": 0.1, "MinPriceChange": 1.5, "MaxPriceChange": 12 }, { "Signal": "TV-1d", "MinPriceChange": 5, "MaxPriceChange": 20 }, { "MinGlobalRating": -0.35, "MaxGlobalRating": 1.0, "MaxSpread": 0.25, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Buy-Bull", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinVolatility": 3, "MaxVolatility": 12, "MaxPriceChange": 5 }, { "Signal": "TV-15m", "MinRating": 0.30, "MaxPriceChange": 5 }, { "Signal": "TV-1h", "MinRating": 0.25, "MinVolume": 100000, "MaxPriceChange": 8 }, { "Signal": "TV-4h", "MinRating": 0.1, "MinPriceChange": 2, "MaxPriceChange": 12 }, { "Signal": "TV-1d", "MinPriceChange": 4, "MaxPriceChange": 20 }, { "MinGlobalRating": 0.25, "MaxSpread": 0.35, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": false, "Name": "Buy-TUSDT", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinRating": 0.30 }, { "Signal": "TV-5m", "MinRating": 0.30 }, { "Signal": "TV-15m", "MinRating": 0.30 }, { "MinGlobalRating": -0.28, "MaxGlobalRating": -0.15, "MaxSpread": 0.35, "Pairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Buy-Volume-Spike", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-5m", "MinRating": 0.4, "MinPriceChange": 1, "MaxPriceChange": 8, "MinVolumeChange": 500, "MaxVolatility": 12 }, { "Signal": "TV-15m", "MinRating": 0.4, "MinPriceChange": 1.5, "MaxPriceChange": 9, "MinVolumeChange": 200 }, { "Signal": "TV-1h", "MinRating": 0.25, "MinVolume": 200000, "MaxPriceChange": 8 }, { "Signal": "TV-4h", "MinPriceChange": 1, "MaxPriceChange": 10 }, { "Signal": "TV-1d", "MinPriceChange": 2, "MaxPriceChange": 20 }, { "MinGlobalRating": -0.10, "MaxGlobalRating": 1.0, "MaxSpread": 1, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Buy-Pump", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-1m", "MinVolumeChange": 20, "MaxVolumeChange": 200, "MinRating": 0.30 }, { "Signal": "TV-5m", "MinRating": 0.30, "MinPriceChange": 3, "MinVolumeChange": 0, "MaxPriceChange": 10 }, { "Signal": "TV-15m", "MinRating": 0.30 }, { "Signal": "TV-1h", "MinRating": 0.0, "MinVolume": 100000, "MaxPriceChange": 8 }, { "Signal": "TV-4h", "MinPriceChange": 3, "MaxPriceChange": 10 }, { "Signal": "TV-1d", "MinPriceChange": 4, "MaxPriceChange": 20 }, { "MinGlobalRating": -0.30, "MaxGlobalRating": 1.0, "MaxSpread": 0.6 } ], "Trailing": { "Enabled": true, "MinDuration": 10, "MaxDuration": 60, "StartConditions": [ { "Signal": "TV-5m", "MinVolumeChange": 200 } ] } }, { "Enabled": true, "Name": "Swap-Safe", "Action": "Swap", "Modifiers": { "CostMultiplier": 1 }, "Conditions": [ { "Signal": "TV-15m", "MinRating": 0.20, "MaxRating": 0.35, "MaxPriceChange": 5 }, { "Signal": "TV-1h", "MinRating": 0.10, "MaxRating": 0.25, "MinVolume": 400000, "MinRatingChange": 0, "MaxPriceChange": 6 }, { "Signal": "TV-4h", "MinRating": 0.10, "MaxRating": 0.25, "MinPriceChange": 1, "MaxPriceChange": 8 }, { "Signal": "TV-1d", "MinRatingChange": 0, "MinPriceChange": 2, "MaxPriceChange": 12 }, { "MinGlobalRating": -0.20, "MaxGlobalRating": 1, "MaxSpread": 0.35, "NotPairs": [ "TUSDBTC" ] } ] } ] }, { "Module": "Trading", "Configuration": { "ProcessingMode": "AllMatches", "CheckInterval": 0.1 }, "Entries": [ { "Enabled": true, "Name": "TUSD-DCA", "Modifiers": { "BuyEnabled": true, "BuyDCAEnabled": true, "SellMargin": 0.20, "SellTrailing": 0.15, "DCALevels": [ { "Margin": -0.40, "BuySamePairTimeout": 0, "BuyTrailing": -0.20, "BuyTrailingStopMargin": 1.00, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.20, "SellTrailing": 0.15 }, { "Margin": -1.25, "BuySamePairTimeout": 180, "BuyTrailing": -0.20, "BuyTrailingStopMargin": 1.50, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.15, "SellTrailing": 0.15 }, { "Margin": -2.50, "BuySamePairTimeout": 300, "BuyTrailing": -0.25, "BuyTrailingStopMargin": 2.00, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.10, "SellTrailing": 0.10 }, { "Margin": -5.50, "BuySamePairTimeout": 1800, "BuyTrailing": -0.25, "BuyTrailingStopMargin": 2.00, "BuyTrailingStopAction": "Cancel", "SellMargin": 0.05, "SellTrailing": 0.10 } ] }, "Conditions": [ { "MinGlobalRating": -1.00, "MaxGlobalRating": -0.10, "Pairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Apocalypse", "Modifiers": { "BuyEnabled": false, "BuyDCAEnabled": false, "SellMargin": -0.50, "SellTrailing": 0, "SellDCAMargin": -0.50, "SellDCATrailing": 0 }, "Conditions": [ { "MinGlobalRating": -1, "MaxGlobalRating": -0.40 } ] }, { "Enabled": true, "Name": "Bear", "Modifiers": { "BuyDCAEnabled": false, "BuyTrailing": -0.45, "SellMargin": 0.40, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.30, "MaxPairs": 4, "DCALevels": [ { "Margin": -4, "SellMargin": 0.20, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.35, "BuySamePairTimeout": 0 }, { "Margin": -8, "SellMargin": 0.15, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.10, "BuyTrailing": -0.55, "BuySamePairTimeout": 600 }, { "Margin": -15, "SellMargin": 0.10, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.10, "BuyTrailing": -1, "BuySamePairTimeout": 1200 }, { "Margin": -20, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": -1.5, "BuySamePairTimeout": 2880 } ] }, "Conditions": [ { "MinGlobalRating": -0.40, "MaxGlobalRating": -0.15, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "BuyDCAEnabled": false, "Name": "Boring", "Modifiers": { "BuyTrailing": -0.35, "SellMargin": 0.50, "SellTrailing": 0.35, "SellTrailingStopMargin": 0.40, "MaxPairs": 5, "DCALevels": [ { "Margin": -2.5, "SellMargin": 0.30, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.30, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -5, "SellMargin": 0.20, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.45, "BuySamePairTimeout": 300 }, { "Margin": -8, "SellMargin": 0.10, "SellTrailing": 0.10, "SellTrailingStopMargin": 0.10, "BuyTrailing": -0.75, "BuySamePairTimeout": 900 }, { "Margin": -12, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": -1.5, "BuySamePairTimeout": 1800 } ] }, "Conditions": [ { "MinGlobalRating": -0.15, "MaxGlobalRating": 0.15, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Bull", "Modifiers": { "BuyTrailing": -0.25, "SellMargin": 0.60, "SellTrailing": 0.45, "SellTrailingStopMargin": 0.50, "MaxPairs": 6, "DCALevels": [ { "Margin": -0.35, "SellMargin": 0.40, "SellTrailing": 0.35, "SellTrailingStopMargin": 0.35, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -2, "SellMargin": 0.35, "SellTrailing": 0.30, "SellTrailingStopMargin": 0.30, "BuyTrailing": -0.35, "BuySamePairTimeout": 180 }, { "Margin": -7, "SellMargin": 0.15, "SellTrailing": 0.15, "SellTrailingStopMargin": 0.15, "BuyTrailing": -0.55, "BuySamePairTimeout": 600 }, { "Margin": -12, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": -1.5, "BuySamePairTimeout": 1400 } ] }, "Conditions": [ { "MinGlobalRating": 0.15, "MaxGlobalRating": 1.00 } ] }, { "Enabled": true, "Name": "DCA", "Modifiers": { "BuyDCAEnabled": false }, "Conditions": [ { "Signal": "TV-15m", "MaxRating": 0.30 }, { "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "SuperBull-HP-DCA", "Modifiers": { "DCALevels": [ { "Margin": -0.25, "SellMargin": 0.25, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.25, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -0.75, "SellMargin": 0.20, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.45, "BuySamePairTimeout": 300 }, { "Margin": -1.50, "SellMargin": 0.15, "SellTrailing": 0.15, "SellTrailingStopMargin": 0.15, "BuyTrailing": -0.65, "BuySamePairTimeout": 600 }, { "Margin": -4.0, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": 2.0, "BuySamePairTimeout": 1200 } ] }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0.40 }, { "Signal": "TV-15m", "MaxRating": 0.40 }, { "Signal": "TV-1h", "MaxRating": 0.40 }, { "MinGlobalRating": 0.30 } ] }, { "Enabled": true, "Name": "Bull-HP-DCA", "Modifiers": { "DCALevels": [ { "Margin": -0.35, "SellMargin": 0.25, "SellTrailing": 0.25, "SellTrailingStopMargin": 0.25, "BuyTrailing": -0.25, "BuySamePairTimeout": 0 }, { "Margin": -1.5, "SellMargin": 0.20, "SellTrailing": 0.20, "SellTrailingStopMargin": 0.20, "BuyTrailing": -0.35, "BuySamePairTimeout": 180 }, { "Margin": -5.5, "SellMargin": 0.15, "SellTrailing": 0.15, "SellTrailingStopMargin": 0.15, "BuyTrailing": -0.55, "BuySamePairTimeout": 300 }, { "Margin": -10, "SellMargin": 0.10, "SellTrailing": 0.05, "SellTrailingStopMargin": 0.05, "BuyTrailing": 2.0, "BuySamePairTimeout": 1200 } ] }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0.40 }, { "Signal": "TV-15m", "MaxRating": 0.40 }, { "Signal": "TV-1h", "MaxRating": 0.30 }, { "MinGlobalRating": 0.20, "MaxGlobalRating": 0.30 } ] }, { "Enabled": true, "Name": "Swap-Only-Bull", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe" ], "SwapTimeout": 1800 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0 }, { "Signal": "TV-15m", "MaxRating": 0 }, { "MaxDCALevel": 2, "MinGlobalRating": 0.30, "MaxGlobalRating": 1.00 } ] }, { "Enabled": true, "Name": "Swap-Money-Pump", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Pump" ], "SwapTimeout": 300 }, "Conditions": [ { "Signal": "TV-15m", "MaxRating": 0 }, { "MaxDCALevel": 0, "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Small-Bag", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Volume-Spike", "Buy-Safe" ], "SwapTimeout": 900 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": -0.30 }, { "Signal": "TV-15m", "MaxRating": -0.30 }, { "Signal": "TV-1h", "MaxRating": 0 }, { "MaxDCALevel": 1, "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Medium-Bag", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe" ], "SwapTimeout": 3600 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": -0.2 }, { "Signal": "TV-15m", "MaxRating": -0.2 }, { "Signal": "TV-1h", "MaxRating": 0 }, { "Signal": "TV-4h", "MaxRating": 0 }, { "MaxDCALevel": 2, "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Big-Bag", "Modifiers": { "BuyDCAEnabled": false, "SwapEnabled": true, "SwapSignalRules": [ "Swap-Safe" ], "SwapTimeout": 7200 }, "Conditions": [ { "Signal": "TV-15m", "MaxRating": -0.2 }, { "Signal": "TV-1h", "MaxRating": 0 }, { "Signal": "TV-4h", "MaxRating": 0 }, { "Signal": "TV-1d", "MaxRating": 0 }, { "MinDCALevel": 3 }, { "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-Run-Forest-Run", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe", "Swap-Safe" ], "SwapTimeout": 300 }, "Conditions": [ { "Signal": "TV-5m", "MaxRating": 0 }, { "MinGlobalRating": -0.20, "MaxGlobalRating": 1.00, "MinMarginChange": 3, "NotPairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Swap-TUSDT", "Modifiers": { "SwapEnabled": true, "SwapSignalRules": [ "Buy-Safe", "Buy-Volume-Spike" ], "SwapTimeout": 300 }, "Conditions": [ { "MinGlobalRating": -0.10, "MaxGlobalRating": 1.00, "Pairs": [ "TUSDBTC" ] } ] }, { "Enabled": true, "Name": "Exclude-BNB", "Modifiers": { "BuyEnabled": false, "BuyDCAEnabled": false, "SellEnabled": false }, "Conditions": [ { "Pairs": [ "BNBBTC" ] } ] }, { "Enabled": true, "Name": "BNB-Top-Up", "Modifiers": { "BuyEnabled": true, "BuyDCAEnabled": true, "BuyDCASamePairTimeout": 0, "BuyMaxCost": 0.003, "RepeatLastDCALevel": true, "DCALevels": [ { "Margin": 100 } ] }, "Conditions": [ { "Pairs": [ "BNBBTC" ], "MaxAmount": 1 } ] }, { "Enabled": true, "Name": "Exclude-Pairs", "Modifiers": { "BuyEnabled": false }, "Conditions": [ { "Pairs": [ "ADABTC", "BCHBTC", "ETHBTC", "XRPBTC", "LTCBTC", "EOSBTC", "XMRBTC", "ZECBTC", "ICXBTC", "DASHBTC", "XLMBTC", "OAXBTC", "RCNBTC", "QSPBTC", "SUBBTC", "BNTBTC", "AEBTC", "TNBBTC", "XEMBTC", "TNTBTC", "TRXBTC", "IOTABTC", "AMBBTC", "DLTBTC", "CDTBTC", "CNDBTC", "CHATBTC", "REQBTC", "CVCBTC", "DNTBTC", "IOTXBTC", "RPXBTC", "VIBBTC" ], "MaxGlobalRating": 0.3 } ] }, { "Enabled": false, "Name": "Arbitrage", "Modifiers": { "BuyEnabled": true, "ArbitrageEnabled": true, "ArbitrageMarkets": [ "ETH", "BNB" ], "ArbitrageBuyMultiplier": 0.985, "ArbitrageSellMultiplier": 0.985, "ArbitrageSignalRules": [ "Buy-Arbitrage" ] }, "Conditions": [ { "MinArbitrage": 3 } ] } ] } ] } } ================================================ FILE: Publish/config/signals.json ================================================ { "Signals": { "Enabled": true, "GlobalRatingSignals": [ "TV-5m", "TV-15m", "TV-1h" ], "Definitions": [ { "Name": "TV-1m", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 1, "SignalPeriod": 1, "VolatilityPeriod": "Day", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-5m", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 3, "SignalPeriod": 5, "VolatilityPeriod": "Day", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-15m", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 5, "SignalPeriod": 15, "VolatilityPeriod": "Week", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-1h", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 7, "SignalPeriod": 60, "VolatilityPeriod": "Month", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-4h", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 15, "SignalPeriod": 240, "VolatilityPeriod": "Month", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } }, { "Name": "TV-1d", "Receiver": "TradingViewCryptoSignalReceiver", "Configuration": { "PollingInterval": 60, "SignalPeriod": 1440, "VolatilityPeriod": "Month", "RequestUrl": "https://scanner.tradingview.com/crypto/scan", "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]}" } } ] } } ================================================ FILE: Publish/config/trading.json ================================================ { "Trading": { "Enabled": true, "Market": "BTC", "Exchange": "Binance", "MaxPairs": 5, "MinCost": 0.000999, "TradePriceType": "Bid", "ExcludedPairs": [], "BuyEnabled": true, "BuyType": "Market", "BuyMaxCost": 0.03, "BuyMultiplier": 1, "BuyMinBalance": 0, "BuySamePairTimeout": 15, "BuyTrailing": -0.25, "BuyTrailingStopMargin": 1.35, "BuyTrailingStopAction": "Buy", "BuyDCAEnabled": true, "BuyDCAMultiplier": 1, "BuyDCAMinBalance": 0, "BuyDCASamePairTimeout": 180, "BuyDCATrailing": -0.25, "BuyDCATrailingStopMargin": 2, "BuyDCATrailingStopAction": "Buy", "SellEnabled": true, "SellType": "Market", "SellMargin": 1.0, "SellTrailing": 0.65, "SellTrailingStopMargin": 0.7, "SellTrailingStopAction": "Sell", "SellStopLossEnabled": false, "SellStopLossAfterDCA": false, "SellStopLossMinAge": 0, "SellStopLossMargin": -3, "SellDCAMargin": 0.5, "SellDCATrailing": 0.35, "SellDCATrailingStopMargin": 0.5, "SellDCATrailingStopAction": "Sell", "RepeatLastDCALevel": false, "DCALevels": [], "TradingCheckInterval": 0.1, "AccountRefreshInterval": 360, "AccountInitialBalance": 1, "AccountInitialBalanceDate": "2018-04-08T00:00:00+00:00", "AccountFilePath": "data/exchange-account.json", "VirtualTrading": true, "VirtualTradingFees": 0.0005, "VirtualAccountInitialBalance": 1, "VirtualAccountFilePath": "data/virtual-account.json" } } ================================================ FILE: Publish/config/web.json ================================================ { "Web": { "Enabled": true, "DebugMode": false, "ReadOnlyMode": false, "Port": 7000, "SSLEnabled": false, "SSLCertPath": "data/cert.pfx", "SSLCertPassword": "certpass" } } ================================================ FILE: Publish/data/encrypt-keys.bat ================================================ dotnet ..\bin\IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key pause ================================================ FILE: Publish/data/encrypt-keys.sh ================================================ #!/bin/bash dotnet ../bin/IntelliTrader.dll --encrypt --path=keys.bin --publickey=public_key --privatekey=private_key ================================================ FILE: Publish/data/pm2.IntelliTrader.json ================================================ { "apps": [ { "name": "IntelliTrader", "cwd": ".", "script": "bin/IntelliTrader.dll", "node_args": [], "log_date_format": "YYYY-MM-DD HH:mm Z", "exec_interpreter": "dotnet", "exec_mode": "fork", "autorestart": false } ] } ================================================ FILE: Publish.bat ================================================ RMDIR /s /q "Publish/bin" dotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile="IntelliTrader/Properties/PublishProfiles/FolderProfile.pubxml" -o "../Publish/bin" dotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile="IntelliTrader.Web/Properties/PublishProfiles/FolderProfile.pubxml" -o "../Publish/bin" ECHO "All done!" PAUSE ================================================ FILE: Publish.sh ================================================ #!/bin/bash dotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile="IntelliTrader/Properties/PublishProfiles/FolderProfile.pubxml" -o "../Publish/bin" dotnet publish -f netcoreapp2.1 -c Release /p:PublishProfile="IntelliTrader.Web/Properties/PublishProfiles/FolderProfile.pubxml" -o "../Publish/bin" ================================================ FILE: README.md ================================================ logo IntelliTrader =========== Overview ------------- IntelliTrader 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. ##### Main Features * Cross-platform * Highly configurable * Easy to use, fast and lightweight * Ability to adjust to different market conditions * Virtual/paper trading support * Backtesting support ##### Additional Resources * logo [Official Website](http://intellitrader.io) * logo [Discord Channel](https://discord.gg/VJZGvrJ) * logo [Youtube Channel](https://www.youtube.com/channel/UC8Gvv0ArdF9a2CHUPTdqkjg) * logo [Medium](https://medium.com/@intellitrader.io/) * logo [TradingView Scripts](https://www.tradingview.com/scripts/search/intellitrader) Getting Started ------------- #### Prerequisites ###### Windows, Linux & MacOS Download and install .NET Core Runtime 2.1 from [Microsoft](https://www.microsoft.com/net/download/all). #### Building You 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: 1. Clone the IntelliTrader repository locally 2. From Git terminal, run 'git submodule update --init --recursive' to pull all the necessary submodules 3. Run Publish.bat (or Publish.sh if you are on Linux) and it should generate the bin folder in the Publish directory. 4. That should be it, try running the bot with IntelliTrader to see if it worked. #### Setting Up The 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). Refer to [web configuration](#web-configuration) section for information on how to do this. #### Running Simply run IntelliTrader to start the bot. Read [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. #### Supported Exchanges Currenly only Binance Exchange is supported. Configuration ------------- All changes to the configuration files will take effect immediately. Configuration quick links: [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) #### Value Types |Type|Example|Description| |-|:-:|-| |Number|0.40|Numeric value| |Boolean|true|Boolean value (true/false)| |String|Market|String value| |Array|[ "BNBBTC", "ETHBTC" ] |Array of numbers, booleans or strings| |Object|{ "Key": "Value" }|Json object| #### Core Configuration |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |DebugMode|Boolean|true|Enable / disable debug mode| |PasswordProtected|Boolean|true|Use password authentication| |Password|String|MD5 hash|MD5-encrypted password| |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)| |TimezoneOffset|Number|1|Timezone offset from UTC (in hours), used by stats| |HealthCheckEnabled|Boolean|true|Enable / disable health check| |HealthCheckInterval|Number|180|Interval to check that all the data is up to date and services are running correctly (in seconds)| |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 |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| #### Web Configuration Read more about how web interface works in the [web](#web-interface) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable web interface| |DebugMode|Boolean|true|Enable / disable debug mode| |ReadOnlyMode|Boolean|false|Enable / disable read only mode| |Port|Number|7000|Port on which to host the web interface| |SSLEnabled|Boolean|false|Enable SSL for web interface| |SSLCertPath|String|data/cert.pfx|Path to the SSL certificate| |SSLCertPassword|String|certpass|Certificate password| #### Signals Configuration Read more about how signals work in the [signals](#signals) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable signals| |GlobalRatingSignals|Array|"TV-5m","TV-15m","TV-1h"|Signals to calculate the Global Rating from| |Definitions|Array|[Signal Definitions](#signal-definitions)|Signal source definitions| ###### Signal Definitions |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Name|String|"TV-15m"|Signal name| |Receiver|String|"TradingViewCryptoSignalReceiver"|Signal receiver name| |Configuration|Object|[Signal Receiver Configuration](#signal-receiver-configuration)|Signal receiver configuration| ###### Signal Receiver Configuration |Setting|Type|Default Value|Description| |-|:-:|-|-| |PollingInterval|Number|7|How often to check for latest signals (in seconds)| |SignalPeriod|Number|15|Signal period (in minutes). Possible values: 5, 15, 60, 240, 1440| |VolatilityPeriod|String|"Day"|Volatility period. Possible values: Day, Week, Month| |RequestUrl|String|"https://scanner.tradingview.com/crypto/scan"|Request url| |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| #### Trading Configuration Read more about how trading works in the [trading](#trading) section. ###### General |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable trading| |Market|String|"BTC"|Market to trade on - BTC, ETH, USDT, etc.| |Exchange|String|"Binance"|Exchange to trade on| |MaxPairs|Number|16|Maximum pairs to trade with| |MinCost|Number|0.000999|Ignore pairs with the specified market value or lower (dust)| |ExcludedPairs|Array|[ "BNBBTC" ]|Pairs excluded from trading| |TradePriceType|String|"Last"|Price type to use for trading. Available values: Last, Ask, Bid| ###### Buying |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |BuyEnabled|Boolean|true|Enable / disable buying| |BuyType|String|"Market"|Supported types: Market. Limit is not currently supported| |BuyMaxCost|Number|0.0012|Maximum cost when buying a new pair| |BuyMultiplier|Number|1|Maximum cost multiplier| |BuyMinBalance|Number|0|Minimum account balance to buy new pairs| |BuySamePairTimeout|Number|900|Rebuy same pair timeout (in seconds)| |BuyTrailing|Number|-0.15|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)| |BuyTrailingStopMargin|Number|0.05|Stop trailing and place buy order immediately when margin hits the specified value| |BuyTrailingStopAction|String|"Buy"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel| ###### Buying DCA |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |BuyDCAEnabled|Boolean|true|Enable / disable buying DCA| |BuyDCAMultiplier|Number|1|Current cost multiplier. To double down set to 1| |BuyDCAMinBalance|Number|0|Minimum account balance to DCA| |BuyDCASamePairTimeout|Number|4200|Rebuy same pair timeout (in seconds)| |BuyDCATrailing|Number|-1.50|Buy trailing percentage (should be a negative number, set to 0 to disable trailing)| |BuyDCATrailingStopMargin|Number|0.40|Stop trailing and place buy order immediately when margin hits the specified value| |BuyDCATrailingStopAction|String|"Cancel"|Action to take after hitting the StopMargin. Possible values: Buy, Cancel| ###### Selling |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |SellEnabled|Boolean|true|Enable / disable selling| |SellType|String|"Market"|Supported types: Market. Limit is not currently supported| |SellMargin|Number|2.00|Minimum percentage increase to start trailing| |SellTrailing|Number|0.70|Sell trailing percentage (should be a positive number, set to 0 to disable trailing)| |SellTrailingStopMargin|Number|1.70|Stop trailing and place sell order immediately when margin hits the specified value| |SellTrailingStopAction|String|"Sell"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel| |SellStopLossEnabled|Boolean|false|Enable / disable stop loss trigger| |SellStopLossAfterDCA|Boolean|true|Trigger stop loss only after all DCA levels has been reached| |SellStopLossMinAge|Number|3.0|Minimum number of days needed before triggering the stop loss| |SellStopLossMargin|Number|-20|Trigger stop loss and immediately place sell order at the specified percentage decrease| ###### Selling DCA |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |SellDCAMargin|Number|1.50|Minimum percentage increase to start trailing| |SellDCATrailing|Number|0.50|Sell trailing percentage (set to 0 to disable trailing) |SellDCATrailingStopMargin|Number|1.25|Stop trailing and place sell order immediately when margin hits the specified value| |SellDCATrailingStopAction|String|"Sell"|Action to take after hitting the StopMargin. Possible values: Sell, Cancel| |RepeatLastDCALevel|Boolean|fasle|Repeat the last DCA Level indefinitely, essentially making the DCA level number unlimited| |DCALevels|Array|[DCA Levels](#dca-levels)|Action to take after hitting the StopMargin. Possible values: Sell, Cancel| ###### DCA Levels Read more about how DCA works in the [DCA](#dca) section. DCALevels 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. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Margin|Number|-1.50|When to trigger the DCA| |BuyMultiplier|Number|BuyDCAMultiplier|Current cost multiplier. To double down set to 1 (Optional)| |BuySamePairTimeout|Number|BuyDCASamePairTimeout|Rebuy same pair timeout (in seconds) (Optional)| |BuyTrailing|Number|BuyDCATrailing|Buy trailing percentage (should be a negative number, set to 0 to disable trailing) (Optional)| |BuyTrailingStopMargin|Number|BuyDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)| |BuyTrailingStopAction|String|BuyDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Buy, Cancel (Optional)| |SellMargin|Number|SellDCAMargin|Minimum percentage increase to start trailing (Optional)| |SellTrailing|Number|SellDCATrailing|Sell trailing percentage (set to 0 to disable trailing) (Optional)| |SellTrailingStopMargin|Number|SellDCATrailingStopMargin|Stop trailing and place buy order immediately when margin hits the specified value. (Optional)| |SellTrailingStopAction|String|SellDCATrailingStopAction|Action to take after hitting the StopMargin. Possible values: Sell, Cancel (Optional)| ###### Accounts |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |TradingCheckInterval|Number|1|Tickers check frequency (in seconds)| |AccountRefreshInterval|Number|360|Exchange account refresh interval (in seconds)| |AccountInitialBalance|Number|0.12|Initial balance on the account, used for stats calculations| |AccountInitialBalanceDate|String|"2018-04-08T00:00:00+00:00"|Date of the initial balance snapshot| |AccountFilePath|String|"data/exchange-account.json"|Path to the account file| |VirtualTrading|Boolean|true|Enable / disable virtual trading| |VirtualTradingFees|Number|0.0005|Trading fees (percentage)| |VirtualAccountInitialBalance|Number|0.12|Initial balance on the virtual account, used for stats calculations| |VirtualAccountFilePath|String|"data/virtual-account.json"|Path to the virtual account file| #### Rules Configuration Read more about how rules work in the [rules](#rules) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Modules|Array|[Module](#module)|Rule Modules| ###### Module |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Module|String|"Module"|Module name| |Configuration|Object|[Module Configuration](#module-configuration)|Module configuration| |Entries|Array|[Rules Configuration](#rules-configuration)|Rules configuration| ###### Module Configuration |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |ProcessingMode|String|"AllMatches"|Available modes: AllMatches (all enabled rules will get processed) or FirstMatch (stop processing at the first rule that is satisfied)| |CheckInterval|Number|3|Rules check frequency (in seconds)| ###### Rules Configuration |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enaled|Boolean|true|Enable / disable rule| |Name|String|Default|Rule name| |Modifiers|Object|[Rule Modifiers](#rule-modifiers)|Rule modifiers| |Conditions|Array|[Rule Conditions](#rule-conditions)|Rule conditions| |Trailing|Object|[Rule trailing](#rule-trailing)|Rule trailing| ###### Rule Modifiers All modifiers (both signal and trading) are optional and can be omitted. Signal modifiers currently only support the *CostMultiplier* (MaxCost multiplier) Trading modifiers support any trading setting that begins with Buy, Sell plus DCALevels and MaxPairs in addition to the following trading-rule specific modifiers: |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |SwapEnabled|Boolean|true|Swap badly performing pairs for better performing ones| |SwapSignalRules|Array|[ "Swap" ]|Rules used to buy the replacement pair with| |SwapTimeout|Number|10800|How long to wait before making a swap (in seconds)| |ArbitrageEnabled|Boolean|false|Enable arbitrage for that pair| |ArbitrageMarkets|Array|[ "ETH" ]|Markets to use for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be used| |ArbitrageType|String|"Reverse"|Type of arbitrage to use. Available values: Direct, Reverse. When omitted both types will be used| |ArbitrageBuyMultiplier|Number|0.99|Percentage of the arbitrage pair to buy| |ArbitrageSellMultiplier|Number|0.99|Percentage of the arbitrage pair to sell| |ArgbitrageSignalRules|Array|[ "Arbitrage" ]|Rules used to arbitrage pairs| ###### Rule Conditions Rule is considered satisfied when all the conditions are met. When trailing is enabled, its conditions must be met first. You can have multiple sets of conditions within a single rule. Is is not necessary to specify a Signal if none of the conditions are signal-specific. |Setting|Type|Signal Specific|Description| |-|:-:|:-:|-| |Signal|String|N/A|Signal with which to calculate signal-specific conditions| |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| |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| |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+| |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| |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| |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| |MinPrice|Number|No|Minimum current price of the coin| |MaxPrice|Number|No|Maximum current price of the coin| |MinPriceChange|Number|Yes|The minimal rate of change of price withing specified period frame (percentage)| |MaxPriceChange|Number|Yes|The maximal rate of change of price withing specified period frame (percentage)| |MinSpread|Number|No|Minimum difference between current bid and ask price (percentage)| |MaxSpread|Number|No|Maximum difference between current bid and ask price (percentage)| |MinVolume|Number|Yes|Minimum coin volume within the specified signal's period. Do not expect 24h volume in this category| |MaxVolume|Number|Yes|Maximum coin volume within the specified signal's period. Do not expect 24h volume in this category| |MinVolumeChange|Number|Yes|The minimal rate of change of volume withing specified period frame (percentage)| |MaxVolumeChange|Number|Yes|The maximal rate of change of volume withing specified period frame (percentage)| |MinVolatility|Number|Yes|Minimum average volatility of a coin within its own specified timeframe| |MaxVolatility|Number|Yes|Maximum average volatility of a coin within its own specified timeframe| |MinAge|Number|No|Minimum trading pair's age (in days, e.g. 1.5 is 36 hours)| |MaxAge|Number|No|Maximum trading pair's age (in days, e.g. 1.5 is 36 hours)| |MinLastBuyAge|Number|No|Minimum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)| |MaxLastBuyAge|Number|No|Maximum trading pair's age since last buy (in days, e.g. 1.5 is 36 hours)| |MinMargin|Number|No|Minimum trading pair's margin| |MaxMargin|Number|No|Maximum trading pair's margin| |MinMarginChange|Number|No|Minimum trading pair's margin change since last buy| |MaxMarginChange|Number|No|Maximum trading pair's margin change since last buy| |MinAmount|Number|No|Minimum trading pair's total purchase amount| |MaxAmount|Number|No|Maximum trading pair's total purchase amount| |MinCost|Number|No|Minimum trading pair's total current cost| |MaxCost|Number|No|Maximum trading pair's total current cost| |MinDCALevel|Number|No|Minimum trading pair's DCA level| |MaxDCALevel|Number|No|Maximum trading pair's DCA level| |MinArbitrage|Number|No|Minimum triangular arbitrage value| |MaxArbitrage|Number|No|Maximum triangular arbitrage value| |ArbitrageMarket|String|No|Market to look for the arbitrage. Available values: ETH, BNB, USDT. When omitted all markets will be considered| |ArbitrageType|String|No|Type of arbitrage to look for. Available values: Direct, Reverse. When omitted both types will be considered| |Pairs|Array|No|List of pairs to directly apply the rule to| |NotPairs|Array|No|List of pairs to not apply the rule to| |SignalRules|Array|No|List of signal rules that were used to buy a pair| |NotSignalRules|Array|No|List of signal rules that were not used to buy a pair| ###### Rule Trailing Trailing is optional and is only supported by the Signal Rules. Trailing starts when all the Rule Trailing StartConditions are met. Trailing ends when all the conditions of the rule are met, at any point between MinDuration and MaxDuration. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|true|Enable / disable trailing| |MinDuration|Number|25|Minimum trail duration (in seconds)| |MaxDuration|Number|240|Maximum trail duration (in seconds)| |StartConditions|Array|[Rule Conditions](#rule-conditions)|Begin trailing when all the below conditions are met.| #### Notification Configuration Read more about how nofitications work in the [notifications](#notifications) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|false|Enable / disable notifications| |TelegramEnabled|Boolean|true|Enabled / disable Telegram notifications| |TelegramBotToken|String|-|Your Telegram bot's token| |TelegramChatId|Number|-|Your Telegram chat id| |TelegramAlertsEnabled|Boolean|true|Enable phone alerts with Telegram messages| #### Backtesting Configuration Read more about how backtesting works in the [backtesting](#backtesting) section. |Setting|Type|Default Value|Description| |-|:-:|:-:|-| |Enabled|Boolean|false|Enable / disable backtesting| |Replay|Boolean|false|Enabled / disable snapshots replay| |ReplayOutput|Boolean|true|Display replay output in the log| |ReplaySpeed|Number|500|Replay speed (1 = SnapshotInterval speed)| |ReplayStartIndex|Number|null|Snapshot index to start replay with| |ReplayEndIndex|Number|null|Snapshot index to end replay with| |DeleteLogs|Boolean|false|Delete all existing logs before running backtesting| |DeleteAccountData|Boolean|false|Delete account data before running backtesting| |CopyAccountDataPath|String|null|Path to copy existing account data file from for backtesting| |TradingSpeedEasing|Number|0|Slow down the trading service while replaying snapshots| |TradingRulesSpeedEasing|Number|0|Slow down the trading rules service while replaying snapshots| |SignalRulesSpeedEasing|Number|0|Slow down the signal rules service while replaying snapshots| |SnapshotsInterval|Number|1|How often to take snapshots (in seconds)| |SnapshotsPath|String|data/backtesting|Where to save the new snapshots or to load existing ones when replaying| #### Other Configuration ###### Exchange Exchange-specific configuration. Don't change the default values unless you know what you're doing. ###### Logging Logging configuration. Here you can disable logging completely, change the default log file paths, verbosity and format. ###### Paths Paths to all the configuration files. Do not change unless you know what you're doing. ###### Caching Currently not in use. ###### Integration Currently not in use. Trading -------------- There are two ways you can trade with IntelliTrader. Virtual trading is enabled on by default. DO NOT switch to live trading until you are fully confident with the bot and are familiar with the way it does trading. Conceptually 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. #### Virtual Trading Virtual trading is enabled by default. You don't need to provide your API Key to virtual trade. To enable virtual trading, set *VirtualTrading* to true in config/trading.json. You might want to change *VirtualAccountInitialBalance* to reflect your starting balance for testing. #### Live Trading To trade on an exchange, first you need to create an encrypted file that will hold your API keys. Open data/encrypt-keys and change public_key to your API key and private_key to your API secret, then run it. You 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. Important: 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. Also, to make the profit stats accurate, you need to set *AccountInitialBalance* to your current BTC/ETH balance (depending on the market you use). That's it, you are ready for exchange trading! #### Trailing When 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. Example trailing story for value -1 (percent): *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.* #### Signals Signals are used to buy new pairs based on the predefined rules. Read more about how signal rules work in the [signal rules](#signal-rules) section. #### Global Rating An average value of all the ratings combined (for current market & exchange) #### DCA Buy additional position at a lower price than the original purchase price. This brings the average price you've paid the pair down. #### Stop Loss Sell a pair at a specified negative margin to avoid it dipping even lower. #### Pair Swapping Swap badly performing pairs for better performing ones. You 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. #### Arbitrage *TODO: Add detailed explanation here* #### Backtesting Backtesting 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. Rules ------------- #### Signal Rules Signal Rules are used to buy new pairs based on the specific conditions. This can also optionally have trailing. If 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. If trailing isn't enabled it buys based only on the specific conditions set. Add "Action": "Swap" if you want the signal rule to only be used for pair swapping. Add "Action": "Arbitrage" if you want the signal rule to only be used for arbitrage. #### Trading Rules Trading 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. Available modifiers for Trading Rules are any trading.json setting that begins with Buy, Sell or DCALevels. This is also where you would setup your Swap Specific Rules. Web Interface ------------- To access the web interface, simply open http://localhost:7000 in your browser (or replace 7000 with the port you have configured). #### Status Bar Status 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. #### Dashboard This 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. Columns 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. You 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. The log button will display the last 5 lines from the log file. |Column|Description| |-|-| |More|Displayed on low resolution screens and when clicked shows additional data| |Pair #1|Pair's name| |Pair #2|Pair's name, including the current DCA level| |DCA|Current DCA level| |Margin|Current profit percentage. If you sold right now, this is what you would make, approximately| |Target|Target sell point in profit. Also known as the profit margin| |Rating|Pair's current rating based on the signals source, Green means it is currently higher than the purchase rating| |Rating Bought|Rating that the pair was purchased at| |Age|How long a pair has been held for| |Amount|Total amount of the pair you currently hold| |Cost|Current value of the pair| |Cost Bought|Purchase value of the pair| |Price|Current price of the pair on the exchange| |Price Bought|Price paid on purchase| |Spread|Difference between current bid and ask price| |Signal Rule|Signal rule used to buy the pair| |Trading Rules|Trading rules currently applied to the pair| |Order Dates|The dates of the purchase orders| |Order IDs|The order Ids on the exchange| Along 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. #### Market This 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. Columns 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. You 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. The log button will display the last 5 lines from the log file. |Column|Description| |-|-| |More|Displayed on low resolution screens and when clicked shows additional data| |Name|Pair's name| |Rating|Ratings for each signal| |% Rating Change|Percentage rating change for each signal| |Price|Current pair's price (same for every signal)| |% Price Change|Percentage price change for each signal| |Spread|Difference between current bid and ask price| |Arbitrage|Triangular arbitrage values for every available market| |Volume|Volume for each signal| |% Volume Change|Percentage volume change for each signal| |Volatility|Volatility for each signal| |Trading Rules|Currently applied trading rules| |Signal Rules|Currently applied signal rules| #### Stats Here you can see your total overall profit and current account balance. There is also a breakdown of profits by day, along with other information, like average margin. You can click on the number of trades to see information about the orders completed on that particular day. You can also open the Rules Analyzer page to check how each rule is performing. #### Settings Here you can temporarily enable or disable a small subset of bot options. If 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). There are also three options available on that page: - 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. - 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. - Log Out: log out of IntelliTrader #### Log Displays the last 500 lines from the log file. #### Help This page Notifications ------------- #### Telegram In 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. Talk to @botfather to create a new bot and then talk to @FalconGate_Bot to get your telegram chat id (type /get\_my\_id). Health Checks ------------- Health 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. Troubleshooting ------------- #### IntelliTrader is not starting or crashing on startup Please 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. #### IntelliTrader is not trading or is behaving weirdly in general Again, check the log file for any errors, it is very likely that there will be a clue there. License & Disclaimer ------------- By using, or simply downloading IntelliTrader (the Software), you understand and accept the following: Licensing The 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: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode A full copy of the license is also included with the download. Limitation Of Liability In 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: (i) your access to, or use of, or inability to access or use the Software; (ii) any content of any third party used with the Software; (iii) any content obtained from the Software; and (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. We do not refund losses. Disclaimer Your 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. No warranty of any kind is expressed or implied, that: a) the Software will function, be secure or operate on any nominated platform; b) any errors or defects will be corrected; c) the Software is free of viruses or other harmful components; or d) the results of using the Software will meet your requirements. If you do not agree with any of the above, please do not download or use the Software.