docker files fixes from liaqat
This commit is contained in:
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
35
.github/workflows/caprover.yml
vendored
Normal file
35
.github/workflows/caprover.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: Build & Deploy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Build and test your app (customize as needed)
|
||||||
|
run: |
|
||||||
|
# Add your build and test commands here
|
||||||
|
# For example:
|
||||||
|
# npm install
|
||||||
|
# npm run build
|
||||||
|
# npm run test
|
||||||
|
- name: Create deploy.tar
|
||||||
|
uses: a7ul/tar-action@v1.1.0
|
||||||
|
with:
|
||||||
|
command: c
|
||||||
|
cwd: "./"
|
||||||
|
files: |
|
||||||
|
scripts/build_and_run.sh
|
||||||
|
|
||||||
|
captain-definition
|
||||||
|
outPath: deploy.tar
|
||||||
|
- name: Deploy App to CapRover
|
||||||
|
uses: caprover/deploy-from-github@v1.0.1
|
||||||
|
with:
|
||||||
|
server: '${{ secrets.CAPROVER_SERVER }}'
|
||||||
|
app: '${{ secrets.APP_NAME }}'
|
||||||
|
token: '${{ secrets.MANAGING_APPS }}'
|
||||||
379
.gitignore
vendored
Normal file
379
.gitignore
vendored
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
## 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
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.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/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
bin
|
||||||
|
obj
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Application.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Application.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Common.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Common.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Core.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Core.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Domain.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Domain.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Debug/netstandard2.0/Managing.Infrastructure.Storage.assets.cache
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Debug/netstandard2.0/Managing.Infrastructure.Storage.csprojAssemblyReference.cache
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Managing.Infrastructure.Storage.csproj.nuget.dgspec.json
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Managing.Infrastructure.Storage.csproj.nuget.g.props
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Managing.Infrastructure.Storage.csproj.nuget.g.targets
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/project.assets.json
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/project.nuget.cache
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Application.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Application.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Common.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Common.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Core.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Core.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Domain.dll
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Domain.pdb
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Debug/netstandard2.0/Managing.Infrastructure.Storage.assets.cache
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Debug/netstandard2.0/Managing.Infrastructure.Storage.csprojAssemblyReference.cache
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Managing.Infrastructure.Storage.csproj.nuget.dgspec.json
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Managing.Infrastructure.Storage.csproj.nuget.g.props
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/Managing.Infrastructure.Storage.csproj.nuget.g.targets
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/project.assets.json
|
||||||
|
/Managing/Managing.Infrastructure.Storage/obj/project.nuget.cache
|
||||||
|
/Managing/Managing.Infrastructure.Storage/bin/Debug/netstandard2.0/Managing.Application.dll
|
||||||
|
/Managing/src/Managing.Api/failures.txt
|
||||||
|
/Managing/src/Managing.Api.Workers/failures.txt
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.vs
|
||||||
|
.history
|
||||||
|
../config.json
|
||||||
|
src/config.json
|
||||||
|
src/Managing.WebApp/src/pages/desk/desk.tsx
|
||||||
|
/src/Managing.Infrastructure.Tests/EvmBaseTests.cs
|
||||||
|
src/Managing.Api.Workers/appsettings.json
|
||||||
|
src/config.json
|
||||||
|
src/Managing.Api.Workers/appsettings.Oda-Sandbox.json
|
||||||
|
/src/Managing.WebApp/package-lock.json
|
||||||
61
Documentation.md
Normal file
61
Documentation.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# How to launch the App
|
||||||
|
## Requirements
|
||||||
|
- NET .Core framework
|
||||||
|
- MongoDb
|
||||||
|
- Node.JS
|
||||||
|
- Discord server with API keys
|
||||||
|
- Alchemy Keys
|
||||||
|
|
||||||
|
# First setup
|
||||||
|
|
||||||
|
|
||||||
|
## Generate dev certificate
|
||||||
|
- ```dotnet dev-certs https -ep \.aspnet\https\aspnetapp.pfx -p password```
|
||||||
|
- ```dotnet dev-certs https --trust```
|
||||||
|
|
||||||
|
## Setup docker
|
||||||
|
- Update ```apps\src\Managing.Api\appsettings.MYNAME.json``` with your configuration settings
|
||||||
|
- Update ```apps\src\Managing.Api.Workers\appsettings.MYNAME.json``` with your configuration settings
|
||||||
|
- Then execute from the root of the project : ```.\scripts\docker-deploy-sandbox.cmd```
|
||||||
|
- At this point, only Managing.Api.Workers should be NOT running. It because we didn't create an account.
|
||||||
|
|
||||||
|
## Setup an account
|
||||||
|
- ```cd src/Managing.WebApp```
|
||||||
|
- ```npm run dev```
|
||||||
|
|
||||||
|
- Go to front-end app ```http://localhost:3000/```
|
||||||
|
- If request end up with a 500 error, you should open the url of the container and validate the certificate.
|
||||||
|
- Connect your ETH account
|
||||||
|
- Setup a username for your account
|
||||||
|
- On Account page : Create an EVM account for trading (Not necessary if you don't want to trade)
|
||||||
|
|
||||||
|
## Setup InfluxDb
|
||||||
|
- Go to http://localhost:8086/
|
||||||
|
- Then initialize InfluxDb
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Go to api keys
|
||||||
|
|
||||||
|

|
||||||
|
- Create an apikey and update the ```apps\src\Managing.Api.Workers\appsettings.MYNAME.json``` file with the api key
|
||||||
|
|
||||||
|

|
||||||
|
- Then redeploy by executing : ```.\scripts\docker-deploy-sandbox.cmd```
|
||||||
|
- Check influxdb > buckets > prices-bucket > if there is data
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Verify the logs of the Workers container to see if candles are well inserted
|
||||||
|
- It may take some time to see the candles in the front-end
|
||||||
|
|
||||||
|
## Setup default strategies, scenario and money management
|
||||||
|
- Go to Managing.Api swagger ```https://localhost/index.html```
|
||||||
|
- Execute ```POST /settings```
|
||||||
|

|
||||||
|
|
||||||
|
# Dev
|
||||||
|
## Generate Api Client
|
||||||
|
- Use [NSwag](https://github.com/RicoSuter/NSwag)
|
||||||
|
- Generate a Typescript client with Fetch template
|
||||||
|
- Deposit the file in ```Managing.WebApp\src\generated\```
|
||||||
129
README.md
Normal file
129
README.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# Introduction
|
||||||
|
|
||||||
|
Managing App is a bot management application written using C# and Typescript. First goal was to be able to run an infinite
|
||||||
|
number of Bot without any limitations (will depend on your server capabilities only)
|
||||||
|
It is designed to support multiples exchanges and be controlled via webUI or Discord.
|
||||||
|
It contains bot management, backtesting, scenario management and money management..
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Roadmap
|
||||||
|
|
||||||
|
## v1 - The base
|
||||||
|
- [x] Bot management
|
||||||
|
- [x] Backtesting
|
||||||
|
- [x] MoneyManagement
|
||||||
|
- [x] Strategies systems
|
||||||
|
- [x] WebUI
|
||||||
|
- [x] Robust trading management
|
||||||
|
- [x] Adaptive trading
|
||||||
|
- [x] Account management
|
||||||
|
- [x] Workers (prices, backtests, volumes)
|
||||||
|
|
||||||
|
## v2 - The custody back
|
||||||
|
- [x] Web3 Authentification
|
||||||
|
- [x] Hot-wallets management
|
||||||
|
- [x] Chainlink and Subgraphs feeds
|
||||||
|
- [x] [GMX Contracts integration](https://gmx.io/)
|
||||||
|
|
||||||
|
### v2.1
|
||||||
|
- [x] Tools : RektFees, Spotlight
|
||||||
|
- [ ] Address tracking
|
||||||
|
- [ ] Strategies optimisation
|
||||||
|
- [ ] Trading desk
|
||||||
|
- [ ] Metrics (backtests, portofolio)
|
||||||
|
- [ ] Enhance performances
|
||||||
|
- [ ] Dockerize everything
|
||||||
|
|
||||||
|
### v2.2
|
||||||
|
- [ ] Hedging Bot with Options [HegicOption](https://www.hegic.co/)
|
||||||
|
- [ ] Market making
|
||||||
|
|
||||||
|
## v3 - The bitcoin protocol
|
||||||
|
- [ ] Integrate [LNMarkets](https://lnmarkets.com/)
|
||||||
|
|
||||||
|
## v4 - The omnipotence
|
||||||
|
- [ ] Connect to [Blockstream Satellite](https://blockstream.com/satellite/)
|
||||||
|
|
||||||
|
# Stack
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|

|
||||||
|
|
||||||
|
## Front-end
|
||||||
|
- [Vite.JS](https://vitejs.dev/)
|
||||||
|
- [Tailwindcss](https://tailwindcss.com/)
|
||||||
|
- [Daisy UI](https://daisyui.com/)
|
||||||
|
- [HeroIcon](https://heroicons.com/)
|
||||||
|
- [Toastify](https://fkhadra.github.io/react-toastify/introduction)
|
||||||
|
- [Tradingview Lightweight-charts](https://github.com/tradingview/lightweight-charts)
|
||||||
|
- [Ploty](https://github.com/plotly/react-plotly.js)
|
||||||
|
- [SignalR](https://github.com/dotnet/aspnetcore/tree/main/src/SignalR#readme)
|
||||||
|
- [Wagmi](https://wagmi.sh/)
|
||||||
|
- [Connectkit](https://github.com/family/connectkit)
|
||||||
|
|
||||||
|
## Back-end
|
||||||
|
- .NET 7
|
||||||
|
- [SignalR](https://dotnet.microsoft.com/en-us/apps/aspnet/signalr)
|
||||||
|
- [Discord.Net](https://github.com/discord-net/Discord.Net)
|
||||||
|
- [CryptoExchange.Net](https://github.com/JKorf/CryptoExchange.Net)
|
||||||
|
- [Stock Indicators](https://daveskender.github.io/Stock.Indicators/)
|
||||||
|
- [InfluxDb](https://github.com/influxdata/influxdb-client-csharp)
|
||||||
|
- [Nethereum](https://github.com/Nethereum/Nethereum)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Features
|
||||||
|
## Money Management
|
||||||
|
- Create a defined money management for a given timeframe (StopLoss, TakeProfit, Amount to risk)
|
||||||
|
- Edit a money management configuration
|
||||||
|
- Delete a configuration
|
||||||
|
|
||||||
|
## Strategies
|
||||||
|
- Build a strategy
|
||||||
|
- Delete strategy
|
||||||
|
|
||||||
|
Strategies availables :
|
||||||
|
|
||||||
|
| Strategy | Description | Recommanded values |
|
||||||
|
| ----------- | ----------- | ----------- |
|
||||||
|
| RsiDivergence | Trigger a signal when a divergence occurs on the period | Period : 4 for 6|
|
||||||
|
| RsiDivergenceConfirm | First, detect a divergence and trigger a signal when the divergence is confirmed. The confirmation happen for a LONG when the price close above the candle in divergence. The loopback period is based on the period parameter. | Period : 4 for 6|
|
||||||
|
| MACDCross | Trigger a signal when EMAs cross | FastPeriod : 12, SlowPeriods : 26, SignalPeriods : 9|
|
||||||
|
| SuperTrend | Trigger a SHORT signal when previous candle is above the super trend and the last candle close below the super trend | Period : 10, Multiplier : 3 |
|
||||||
|
| ChandelierExit | Trigger a SHORT signal when previous candle is above the ChandelierExit and the last candle close below the ChandelierExit | Period : 22, Multiplier : 3 |
|
||||||
|
| EMACross | Trigger a signal when last candle cross the EMA | Period : 200 |
|
||||||
|
| EMATrend | Return a Trend signal SHORT when last candle is below the EMA and return a Trend LONG signal when StochRSI < 20% | Period : 200 |
|
||||||
|
| StochRsiTrend | Return a Trend signal SHORT when Stoch RSI > 80% and return a Trend LONG signal when StochRSI < 20% | Period : 22 |
|
||||||
|
| STC | Return a signal SHORT when previous STC > 75% and current STC <= 75% | Period : 22 |
|
||||||
|
|
||||||
|
## Scenarios
|
||||||
|
- Build a scenario with multiple strategies
|
||||||
|
- Delete a scenario
|
||||||
|
|
||||||
|
## Backtests
|
||||||
|
|
||||||
|
The backtest system works with multiple required parameters :
|
||||||
|
- Exchange (Binance, Kraken, FTX)
|
||||||
|
- Ticker (ADAUSDT, BTCUSDT, etc..)
|
||||||
|
- Days : Since when did you want to start backtest. Should be a negative value
|
||||||
|
- ScenarioName
|
||||||
|
- Timeframe (OneDay, FifteenMinutes, etc..)
|
||||||
|
- BotType (ScalpingBot or FlippingBot)
|
||||||
|
- Initial balance
|
||||||
|
|
||||||
|
## Bots
|
||||||
|
|
||||||
|
- Create and run a bot
|
||||||
|
- Stop / Restart a bot
|
||||||
|
- Delete a bot
|
||||||
|
- Stop all bots
|
||||||
|
- Set bot to watch only (send signal to discord instead of opening a new position)
|
||||||
|
|
||||||
|
Bot types availables :
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
| ----------- | ----------- |
|
||||||
|
| ScalpingBot | This bot will open position and wait before opening a new one |
|
||||||
|
| FlippingBot | The flipping bot flipping the position only when a strategy trigger an opposite signal |
|
||||||
|
|
||||||
75
assets/NSwagConfig.nswag
Normal file
75
assets/NSwagConfig.nswag
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"runtime": "Net60",
|
||||||
|
"defaultVariables": null,
|
||||||
|
"documentGenerator": {
|
||||||
|
"fromDocument": {
|
||||||
|
"url": "https://localhost:5001/swagger/v1/swagger.json",
|
||||||
|
"output": null,
|
||||||
|
"newLineBehavior": "Auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"codeGenerators": {
|
||||||
|
"openApiToTypeScriptClient": {
|
||||||
|
"className": "{controller}Client",
|
||||||
|
"moduleName": "",
|
||||||
|
"namespace": "",
|
||||||
|
"typeScriptVersion": 4.3,
|
||||||
|
"template": "Fetch",
|
||||||
|
"promiseType": "Promise",
|
||||||
|
"httpClass": "HttpClient",
|
||||||
|
"withCredentials": false,
|
||||||
|
"useSingletonProvider": false,
|
||||||
|
"injectionTokenType": "OpaqueToken",
|
||||||
|
"rxJsVersion": 6.0,
|
||||||
|
"dateTimeType": "Date",
|
||||||
|
"nullValue": "Undefined",
|
||||||
|
"generateClientClasses": true,
|
||||||
|
"generateClientInterfaces": false,
|
||||||
|
"generateOptionalParameters": false,
|
||||||
|
"exportTypes": true,
|
||||||
|
"wrapDtoExceptions": false,
|
||||||
|
"exceptionClass": "ApiException",
|
||||||
|
"clientBaseClass": null,
|
||||||
|
"wrapResponses": false,
|
||||||
|
"wrapResponseMethods": [],
|
||||||
|
"generateResponseClasses": true,
|
||||||
|
"responseClass": "SwaggerResponse",
|
||||||
|
"protectedMethods": [],
|
||||||
|
"configurationClass": null,
|
||||||
|
"useTransformOptionsMethod": false,
|
||||||
|
"useTransformResultMethod": false,
|
||||||
|
"generateDtoTypes": true,
|
||||||
|
"operationGenerationMode": "MultipleClientsFromFirstTagAndOperationId",
|
||||||
|
"markOptionalProperties": true,
|
||||||
|
"generateCloneMethod": false,
|
||||||
|
"typeStyle": "Interface",
|
||||||
|
"enumStyle": "Enum",
|
||||||
|
"useLeafType": false,
|
||||||
|
"classTypes": [],
|
||||||
|
"extendedClasses": [],
|
||||||
|
"extensionCode": null,
|
||||||
|
"generateDefaultValues": true,
|
||||||
|
"excludedTypeNames": [],
|
||||||
|
"excludedParameterNames": [],
|
||||||
|
"handleReferences": false,
|
||||||
|
"generateConstructorInterface": true,
|
||||||
|
"convertConstructorInterfaceData": false,
|
||||||
|
"importRequiredTypes": true,
|
||||||
|
"useGetBaseUrlMethod": false,
|
||||||
|
"baseUrlTokenName": "API_BASE_URL",
|
||||||
|
"queryNullValue": "",
|
||||||
|
"useAbortSignal": false,
|
||||||
|
"inlineNamedDictionaries": false,
|
||||||
|
"inlineNamedAny": false,
|
||||||
|
"includeHttpContext": false,
|
||||||
|
"templateDirectory": null,
|
||||||
|
"typeNameGeneratorType": null,
|
||||||
|
"propertyNameGeneratorType": null,
|
||||||
|
"enumNameGeneratorType": null,
|
||||||
|
"serviceHost": null,
|
||||||
|
"serviceSchemes": null,
|
||||||
|
"output": "Managing.WebApp/src/generated/",
|
||||||
|
"newLineBehavior": "Auto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
151
assets/Todo-v1.txt
Normal file
151
assets/Todo-v1.txt
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
Principals features :
|
||||||
|
- Create X bots
|
||||||
|
- Each Trading bot can have multiple strategies
|
||||||
|
- Each Strategy is run for every new candle that the bot read from the database
|
||||||
|
- Use exchange worker to retrieve candles for a ticker
|
||||||
|
- Open one click a long with SL and 2 take profit with defined risk
|
||||||
|
|
||||||
|
V1 :
|
||||||
|
|
||||||
|
Part 1 : Running bot
|
||||||
|
- OK - Create no relationnal database to store all tickers history by exchanges
|
||||||
|
- OK - Feed the database with a Bot
|
||||||
|
- OK - On Bot motherclass, create a method accessible to child to retrieve the last candle since a prompted date
|
||||||
|
- OK - Create 3 Soldiers strategy
|
||||||
|
- OK - Create rule "CloseHigherThanThePreviousHigh"
|
||||||
|
- OK - Create rule "CloseLowerThanThePreviousLow"
|
||||||
|
|
||||||
|
Part 2 : Make a trade
|
||||||
|
- OK - Implement a GetBalance with the value in $
|
||||||
|
- OK - Calculate risk per for a given trade
|
||||||
|
- OK - Open a Long with defined risk level and exchange
|
||||||
|
- OK - Define and Open Stop loss with risk level
|
||||||
|
- OK - Define and open 2 Take Profit with risk level
|
||||||
|
- OK - Register generic candle for every exchanges in database
|
||||||
|
- OK - Create an endpoint to retrieve all order (with a filter class)
|
||||||
|
- OK - Create an endpoint to close an order for a given ExchangeOrderId
|
||||||
|
- OK - Implement OpenMarketTrade for Binance Futures
|
||||||
|
- OK - Implement GetPrice for Binance Futures
|
||||||
|
- OK - Implement GetTrade for Binance Futures
|
||||||
|
- OK - Binance : Handle TimeInForce for limit or market trade
|
||||||
|
|
||||||
|
Part 3 : Bot running RSI
|
||||||
|
- OK - Create RSI for 32period
|
||||||
|
- OK - Handle multiple divergence point for a candle range
|
||||||
|
- OK - Create rule to find a divergence for given RSI -> Confidence.High
|
||||||
|
- OK - Create an identifier for each bot based on name-datetime
|
||||||
|
- OK - Save price into Signal Object
|
||||||
|
|
||||||
|
Part 3.1
|
||||||
|
- OK - Each bot should be a manager keep in memory the trades executed and execute trade by calling openMarketPosition()
|
||||||
|
- OK - Create a mother class call TradeManager
|
||||||
|
- OK - This class should access to the signal return by the Scalping bot
|
||||||
|
- OK - This class should handle the trade management
|
||||||
|
- OK - Save Signals into database
|
||||||
|
|
||||||
|
Part 3.2 :
|
||||||
|
- OK - Create a unit test to backtest the bot execution
|
||||||
|
- OK - Get range candle list for a ticker
|
||||||
|
- OK - Each bot should have a backtest mode. This mode allow the caller to insert his own candles into the list. This mode will be used by the unit tests
|
||||||
|
- OK - Foreach candle, update the candle list of the bot and implicitly run all strategies
|
||||||
|
|
||||||
|
- OK - For each last signal for a ticker check if position is open. If not: Open position.
|
||||||
|
- OK - If an position is already open, update the position and check the price and trigger a close/flip if there is an inversed signal for that position.
|
||||||
|
- OK - If there is a inverted signal, close or flip the position depending of the confidence of the signal
|
||||||
|
- OK - Create a collection for registering the position with pnl, status
|
||||||
|
- OK - Save in trade table a reference to the position id, bot identifier
|
||||||
|
- OK - Create class to calculate PNL
|
||||||
|
- OK - openMarketPosition () should handle a parameter to make paper trading: just return the object that represent the trade executed by the exchange. That parameter will be set by bot in backtest mode
|
||||||
|
- OK - Add function to reopen trade that wasnt filled or whatever
|
||||||
|
- OK - Assert the fact that a list of positions have been created and ending in positive
|
||||||
|
- OK - Fix timestamp unix for getting klines for OneMinutes timeframe
|
||||||
|
- OK - Run bot on past candle
|
||||||
|
- OK - Extract trading logic from Bot class, to create a (TradingBot : Bot) class.
|
||||||
|
|
||||||
|
Part 4:
|
||||||
|
- Ok - Create RSIBearishDivergenceStrategy
|
||||||
|
- OK - Move Risk level config into appsettings to manipulate easily the user preference
|
||||||
|
- OK - Implement multi-timeframe to Define MoneyManagement for everyTimeFrame
|
||||||
|
- OK - Handle little quantity for trade by multiplying the quantity inside pnl object
|
||||||
|
- OK - Set the correct quantity for a trade who take profit #1
|
||||||
|
- OK - Add more tests for ProfitAndLoss object
|
||||||
|
- OK - Setup optionnal second take profit
|
||||||
|
- OK - Create DailyBot and Minutes bot to handle 5 ans 14 periods rsi
|
||||||
|
- OK - Run Bot for little TimeFrame
|
||||||
|
- OK - Implement GetTrade for an OrderId on Binance
|
||||||
|
- OK - Add discord connection
|
||||||
|
- OK - Expose all the API on discord bot
|
||||||
|
- OK - Run instance of bot for ADA, 5 periods, 15min
|
||||||
|
- OK - Refactoring bots unit tests to use same method than discord bot
|
||||||
|
- OK - Create CSV file for backtesting report
|
||||||
|
|
||||||
|
Part 5:
|
||||||
|
- OK - Improve open position from discord
|
||||||
|
- OK - Improve multi-risk levels handling
|
||||||
|
- OK - Improve divergence signal by handeling confidence
|
||||||
|
- OK - Implement ExchangeService.CloseTrade for exchanges
|
||||||
|
|
||||||
|
Part 6: webapp
|
||||||
|
- OK - Build front-end app with react
|
||||||
|
- OK - Build Tradingview component
|
||||||
|
- Update trading bot from signalr for everynew run => pass the BotHubContext to ITradingbot interface
|
||||||
|
- OK - Bots page to manage running bots
|
||||||
|
- - OK - Create a modal to start a bot
|
||||||
|
- - OK - On backtest page, display a button to start a bot based on defined backtest configuration
|
||||||
|
- - Handle stop and restart
|
||||||
|
- OK - Create scanner page to load all the ticker backtest base on a setup (timeframe, risk, day, bottype)
|
||||||
|
- OK - Save backtestResult into database to display it into Scanner page
|
||||||
|
- OK - Display backtest result on scanner page
|
||||||
|
- Scenario builder => A component to build nested trading strategy https://www.kindacode.com/article/react-typescript-drag-and-drop/
|
||||||
|
- - OK - Store scenario into database -> a scenario contain a list of defined strategy
|
||||||
|
- - OK - Dynamically insert scenario into ITradingBot
|
||||||
|
- - OK - Create a modal form to build a scenario that contains a list of strategies
|
||||||
|
- - OK - On backtest page, retrieve scenario list to launch a backtest based on it
|
||||||
|
|
||||||
|
Part 7: Improve trading
|
||||||
|
- OK - Handle fees
|
||||||
|
- OK - Create strategy to send rsi div signal on confirmation only
|
||||||
|
- OK - Make flipping position optionnal
|
||||||
|
- OK - Candle mapped on the wrong time => 18h30 should be 18h15
|
||||||
|
- KO - Enable backtesting on very long time range => No historical data more than 1500ohlc per timeframe on FTX
|
||||||
|
|
||||||
|
Part 8 : enhance front
|
||||||
|
- Settings page =>
|
||||||
|
- OK - Put money management into database to be able to modify it without rebuild/restart the api
|
||||||
|
- OK - Create a tab page on settings page to easily update monymanagement in db
|
||||||
|
- Improve card informations for bots
|
||||||
|
- - OK - Display the signals count for long and short
|
||||||
|
- - OK - Add button to toggle start and stop
|
||||||
|
- - OK - Add a toggle for isWatchingOnly
|
||||||
|
- OK - Add filter checkbox on Backtest/Moneymanagement to filter the list
|
||||||
|
- OK - Create a button to delete all backtests
|
||||||
|
- OK - Implement switch network debug/local
|
||||||
|
|
||||||
|
Part 9: Improve bot usage
|
||||||
|
- Save bot config to database to re-run bot on restart
|
||||||
|
- OK - Create endpoint to restart a bot
|
||||||
|
- Ok - Create endpoint to stop all bots
|
||||||
|
- OK - On signal, if a position is open => Same direction : expired signal | opposition direction : Flip open position
|
||||||
|
- OK - Implement a flipping function on an open position
|
||||||
|
- Ok - Track balance evolution to determine max drawdown
|
||||||
|
- OK - Determine best parameter for a given scenario : https://www.youtube.com/watch?v=jm-UfVPqUQo (Start with a unit test)
|
||||||
|
- OK - When a bot start up for a ticker, clean all limit order
|
||||||
|
- OK - When a bot start up for a ticker, clean open position
|
||||||
|
- OK - Before opening a trade clean limit order
|
||||||
|
- OK - Get status of position before closing it. The position might be already close by the exchange
|
||||||
|
|
||||||
|
Part 10: Improve performance
|
||||||
|
- OK - Calculate the strategies only on the last n*10 period
|
||||||
|
- OK - Merge divergence RSI strategy to calculate rsi only once by run
|
||||||
|
- KO - Prevent Order's notional must be no smaller than 5.0
|
||||||
|
- OK - Don't run update Signal and manage position if no new candle
|
||||||
|
|
||||||
|
Part 11: Extra
|
||||||
|
- OK - Create custom exception to handle error
|
||||||
|
- OK - Add ELK stacks
|
||||||
|
- OK - Display R/R used on backtestResult
|
||||||
|
- OK - Update Exchanges libraries
|
||||||
|
- OK - Integrate FTX exchange
|
||||||
|
- OK - Display wallet evolution
|
||||||
|
- OK - Remove API keys from git
|
||||||
|
- OK - Add seed for moneymanagement and scenario
|
||||||
199
assets/Todo-v2.md
Normal file
199
assets/Todo-v2.md
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# V2
|
||||||
|
## Done
|
||||||
|
### Adaptive mode
|
||||||
|
- [x] Create a risk mode 'Adaptive' to be able to not use save moneymanagement settings
|
||||||
|
- [x] Adaptive mode will force Bot to determine SL by getting the previous highest price on the period (prevent choppy price on good entry)
|
||||||
|
- [x] Create account (type, exchange, key, secret)
|
||||||
|
- [x] Update trading bot to handle one account
|
||||||
|
- [x] Remove exchange from bot. Exchange will be set on the account
|
||||||
|
- [x] Update ftxClient to not use Managingbot subaccount
|
||||||
|
- [x] Update exchangeService to handle account
|
||||||
|
- [x] Dynamic Ticker selection base on best volumes on the exchanges
|
||||||
|
- [x] Create endpoint to get the ticker list for the frontend
|
||||||
|
- [x] Create method in application to get best bid and best ask
|
||||||
|
- [x] Submit only limit with IOC
|
||||||
|
- [x] Implement Poly to have a retry policy for limit order ioc execution => Unsure position is open
|
||||||
|
- [x] For openOrder inject a parameter to select the ioc mode. If risk is adaptive we just place the order a the price without forcing the position opening
|
||||||
|
- [x] Integrate STC signals
|
||||||
|
### Web3
|
||||||
|
- [x] Integrate wallet management from backend side
|
||||||
|
- [x] Integrate wagmi/connectkit
|
||||||
|
- [x] Sign message with connectkit
|
||||||
|
- [x] On wallet connect, check if token is not expired
|
||||||
|
- [x] User can have multiple address
|
||||||
|
- [x] User can create an account base on evm exchange
|
||||||
|
- [x] Hide key/secret if evm exchange is selected
|
||||||
|
- [x] Generate pub/priv key on evm account creation and store it into mongo
|
||||||
|
- [x] Get balance for an address
|
||||||
|
- [x] Handle multi-chain
|
||||||
|
- [x] Add arbitrum testnet for backend
|
||||||
|
- [x] User can withdraw found to the connected address of the user
|
||||||
|
- [x] Implement get balance with chainlink feed
|
||||||
|
- [x] Abstract subgraph management
|
||||||
|
- [x] Get price from multiple price feeds subgraph
|
||||||
|
- [x] Add to ExchangeService GetAvailableTicker()
|
||||||
|
- [x] Add get token list method to EvmManager
|
||||||
|
- [x] Setup worker to save candles into db
|
||||||
|
- [x] Open trade -> CreateIncreaseOrder
|
||||||
|
- [x] Close trade -> CreateDecreasePosition
|
||||||
|
- [x] Close all orders
|
||||||
|
- [x] Create SL & TP -> CreateDecreaseOrder
|
||||||
|
- [x] Get Fees -> Estimate Tx cost
|
||||||
|
- [x] Adapt GetBalance to retrieve stablecoin amount
|
||||||
|
- [x] Create worker to fetch GMX leaderboard -> LeaderboardWorker
|
||||||
|
- [x] Leaderboard will create PositionUpdate message bus to deposit a message with all information to open a trade
|
||||||
|
- [x] Create bot type CopyTrading to receive PositionUpdate bus message and open position based on criteria
|
||||||
|
- [x] CopyTrading bot will send an OpenPosition button on Discord if he's set to watchOnly
|
||||||
|
- [x] Fix : Trim address, give name to address, decrease amount bug,
|
||||||
|
- [x] Display best last backtests ran by backtester workers
|
||||||
|
- [x] Refactor GetAccounts/GetAccount to handle the user
|
||||||
|
- [x] AccountType : Binance/EvmTrador/EvmWatch/EvmPersonal
|
||||||
|
- [x] Make better abstration of the Database projects (influx, mongodb) to Remove the link between Application and Infrastructure
|
||||||
|
- [x] Implement strategy pattern for exchange processors
|
||||||
|
- [x] Use cache for position and signal
|
||||||
|
- [x] Don't run update Signal and manage position if no new candle
|
||||||
|
### Workers
|
||||||
|
- [x] Add Influxdb
|
||||||
|
- [x] Create workers for scrap data (per ticker, per exchange, per timeframe)
|
||||||
|
- [x] Use candle from influxdb for backtests and bots
|
||||||
|
- [x] Create a worker that get a daily top 15 of ticker volume
|
||||||
|
- [x] Scrap candle only for crypto with high volume
|
||||||
|
- [x] Create a worker that run backtest and save it in db
|
||||||
|
|
||||||
|
### Front-end
|
||||||
|
- [x] Return list of balances with tokens/chains data
|
||||||
|
- [x] On account line click, display tokens by chain available
|
||||||
|
- [x] Call Available ticker to produce the droplist
|
||||||
|
- [x] Display multipanes https://github.com/qwpto/lightweight-charts-panels / https://codesandbox.io/s/react-typescript-forked-mxek17?file=/src/App.tsx / https://github.com/Rassibassi/multipane_tradingview
|
||||||
|
- [x] Display last open position with lines (add SL and TP)
|
||||||
|
- [x] Fix double charts problem (has been fixed by removing strict mode)
|
||||||
|
- [x] Try to pass the tailwind current theme color to the chart
|
||||||
|
- [x] List all open position for every trading account
|
||||||
|
- [x] Create button to force close position
|
||||||
|
- [x] Open Position button
|
||||||
|
- [x] Improve position message -> Create message buildor
|
||||||
|
- [x] Create channel handler for address watcher
|
||||||
|
- [x] Add input to specify the account balance. If not set, it will use the selected account balance
|
||||||
|
|
||||||
|
# The rest
|
||||||
|
## Part 1 : Adaptive trading
|
||||||
|
- [x] From backtests list : Determine TP and Stop loss % by getting average profit and average lost for a signal
|
||||||
|
- [x] Modify bot to use MoneyManagement object instead of using the risk associated to the signal.
|
||||||
|
- [x] Bot object should return MoneyManagement object
|
||||||
|
- [ ] Strategy can return a suggested trade that will give the best orders to execute
|
||||||
|
|
||||||
|
## Part 2 : Workflows builder
|
||||||
|
- [x] Create a workflow type that will encapsulate a list of flows
|
||||||
|
- [x] Each flow will have parameters, inputs and outputs that will be used by the children flows
|
||||||
|
- [ ] Flow can handle multiple parents
|
||||||
|
- [ ] Run Simple bot base on a workflow
|
||||||
|
- [ ] Run backtest based on a workflow
|
||||||
|
- [ ] Add flows : ClosePosition, Scenario
|
||||||
|
|
||||||
|
## Part 2 : Strategies optimisation
|
||||||
|
- [ ] Optimize all strategies with more data (funding rate trend, bbwp, etc..)
|
||||||
|
- [ ] Optimize entry by getting last ask price and place an order limit to match directly with it
|
||||||
|
- [ ] Add bbwp indicator => PR example : https://github.com/DaveSkender/Stock.Indicators/pull/893/files#diff-df1cd2a99846f7328c9fc3db4d2c164380a0fa943cdcad2ece01b886cac68137
|
||||||
|
- [ ] Create strategies types from signal (a divergence) / trend (stoch long or short) / context (high vol/low vol)
|
||||||
|
- [ ] Create a trend strategy on fundinrate -> long trend if negativ fr / short trend if positiv fr
|
||||||
|
- [ ] Add entry/exit price to signal for adaptive risk
|
||||||
|
- [ ] MACD: Open only when candle is not a big move to avoid late entry (only enter when low vol)
|
||||||
|
- [ ] Improve strategy composability by using delegate pattern
|
||||||
|
|
||||||
|
## Part 3 : Backtests automation
|
||||||
|
- [ ] Create a worker that determine best TP and SL
|
||||||
|
- [ ] Create a page 'Analyze' to show the best TP and SL for multiple backtest result
|
||||||
|
- [ ] Implement reverseSignal to close and flip the trade
|
||||||
|
- [ ] Load signal from database
|
||||||
|
- [ ] Create a bot to update signal into database that still in pending
|
||||||
|
- [ ] Signal return best SL and TP
|
||||||
|
- [ ] Combine multi-timeframe to detect better signals => Adapt risk on the lowest timeframe based on upper timeframe
|
||||||
|
- [ ] Remove candles from the backtestResult
|
||||||
|
- [ ] Save start/end time and timeframe into the result
|
||||||
|
- [ ] On GET backtest -> Retrieve the candle from cache or database
|
||||||
|
|
||||||
|
_______________________________________________________________________________________________________________
|
||||||
|
|
||||||
|
# Phase 2 - Web3
|
||||||
|
## 1 - Wallet management
|
||||||
|
- [ ] User can deposit token to and evm account with a QR code or clicking on button to copy the address
|
||||||
|
## 2 - [Track address](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32158941)
|
||||||
|
- [ ] Create a snapshot of the account balances with BalancesWorker
|
||||||
|
- [ ] Display balance in $ evolution per token hold on the account page
|
||||||
|
## 3 - Settings [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32159287)
|
||||||
|
- [ ] Save theme into database
|
||||||
|
- [ ] Call GET Settings/Theme to retrieve the theme used by the user
|
||||||
|
## 4 - Trading desk [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=11070087)
|
||||||
|
- [x] Live data position hub
|
||||||
|
- [x] Live candles
|
||||||
|
- [x] Open position from desk
|
||||||
|
## 5 - Live liquidation map
|
||||||
|
- [ ] Create worker to aggregate binance/okx liquidation
|
||||||
|
- [ ] Render liquidation map with a hub subscription to get live update
|
||||||
|
- [ ] Live data signal
|
||||||
|
- [ ] Display massive liquidation on TradeChartWidget
|
||||||
|
## 6 - Metrics [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32159107)
|
||||||
|
- [ ] Create a worker that get top 5 cryptos by scenario
|
||||||
|
- [ ] Create worker to store portofolio evolution
|
||||||
|
- [ ] Display portofolio evolution
|
||||||
|
- [ ] Create a worker to scrap funding fees over time (per ticker, per exchange)
|
||||||
|
## 7 - Improvement [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32159127)
|
||||||
|
- [ ] Handling user on every object => Use the JWT token to inject the userName
|
||||||
|
- [ ] Extract BacktestReport from tests to a Tools projects
|
||||||
|
- [ ] Factorize amount management for GMX Service
|
||||||
|
- [ ] Remove link betweend Domain object to EVM project
|
||||||
|
- [ ] GMX Service should handle only Nethereum object, not Domain.Models
|
||||||
|
- [ ] Compute backtests pagination on backend side
|
||||||
|
- [ ] Store webapp into docker
|
||||||
|
- [ ] Make possible to force close all positionpositions of a bot
|
||||||
|
- [ ] Save bot config to easily relaunch a bot and also analyze data
|
||||||
|
- [ ] Bot can handle limit order position -> fetch if order is still open then check if position of open
|
||||||
|
- [ ] Create method to update the money management use by the bot
|
||||||
|
- [ ] Implement from/to tickers array pattern
|
||||||
|
- [ ] Extract all managing trade method into a TradingBox class => Create composable trading bot type easily
|
||||||
|
|
||||||
|
# Front-end
|
||||||
|
## Improve Account page
|
||||||
|
- [ ] Display total balances (only trader account) on Settings->Accounts Page
|
||||||
|
- [ ] On Token Account details line -> Add button to withdraw money (this will open a Modal to prompt the address/amount that will receive the money)
|
||||||
|
## Charts
|
||||||
|
- [ ] Add multi-timeframe options
|
||||||
|
- [ ] Display optionnal indicators on charts
|
||||||
|
- [ ] Display current selected position (when i click on a Position in the list)
|
||||||
|
## Trading desk
|
||||||
|
- [ ] WAIT - Create a button to open a hedge on option market
|
||||||
|
- [ ] Get global exposure ((total position / total balances *) 100)
|
||||||
|
- [ ] Display multiple tab to show different tickers
|
||||||
|
## Scenarios
|
||||||
|
- [x] Create a blocky-like editor to create scenarios https://google.github.io/blockly-samples/
|
||||||
|
## Bots
|
||||||
|
- [ ] Add button to display money management use by the bot
|
||||||
|
- [ ] On the modal, When simple bot selected, show only a select for the workflow
|
||||||
|
## Backtests
|
||||||
|
- [x] Display the money management used by the backtest
|
||||||
|
## Technical front-end :
|
||||||
|
- [ ] Implement alias to reduce 'import' lines https://github.com/subwaytime/vite-aliases
|
||||||
|
- [ ] Make Get Accounts List as a store
|
||||||
|
- [ ] Replace all html-tag Modals (ex: BacktestPlayground.tsx)
|
||||||
|
- [ ] Refactoring Components :
|
||||||
|
- organism : Put all the code related to the app (Trade charts etc). This folder should be delete when the project is forked
|
||||||
|
- mollecules : Put aggregate of atoms to create forms/modal/table
|
||||||
|
- atoms : Put all basic html tag components (List, Select, Slider...)
|
||||||
|
## Workflow
|
||||||
|
- [x] List all workflow saved in
|
||||||
|
- [x] Use https://reactflow.dev/ to display a workflow (map flow to nodes and children to edges)
|
||||||
|
- [x] On update Nodes : https://codesandbox.io/s/dank-waterfall-8jfcf4?file=/src/App.js
|
||||||
|
- [x] Save workflow
|
||||||
|
- [ ] Reset workflow
|
||||||
|
- [ ] Add flows : Close Position, SendMessage
|
||||||
|
- [ ] On Flow.tsx : Display inputs/outputs names on the node
|
||||||
|
- [ ] Setup file tree UI for available flows : https://codesandbox.io/s/nlzui
|
||||||
|
|
||||||
|
_____________________________________________________________________________________________________________
|
||||||
|
|
||||||
|
# Technicals
|
||||||
|
|
||||||
|
- [ ] Remove mediator
|
||||||
|
- [ ] Check Traefik : https://www.cloudbees.com/blog/setting-up-traefik-as-a-reverse-proxy-for-asp-net-applications
|
||||||
|
- [ ] Implement IHostedService/BackgroundService to run bot in background https://www.youtube.com/watch?v=fw-n2RkzOMI
|
||||||
|
- [ ] Implement Microsoft Orleans
|
||||||
1586
assets/documentation/Architecture.drawio
Normal file
1586
assets/documentation/Architecture.drawio
Normal file
File diff suppressed because it is too large
Load Diff
12
assets/documentation/EndGame.md
Normal file
12
assets/documentation/EndGame.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Futures bot] -->|50% weekly withdraw| B[Delta neutral bot]
|
||||||
|
A -->|25% weekly withdraw| F[Stake GMX]
|
||||||
|
A -->|25% weekly withdraw| E
|
||||||
|
B -->|25% profit | A
|
||||||
|
B -->|75% compounding| B
|
||||||
|
D[Hedging bot] -->|hedge open position| A
|
||||||
|
D -->|generate profit| A
|
||||||
|
E[Staking bot] -->|claim ETH rewards| F[Personnal Wallet]
|
||||||
|
|
||||||
|
```
|
||||||
27
assets/documentation/Flows.md
Normal file
27
assets/documentation/Flows.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
Workflow <|-- Flow
|
||||||
|
class Workflow{
|
||||||
|
String Name
|
||||||
|
Usage Usage : Trading|Task
|
||||||
|
Flow[] Flows
|
||||||
|
String Description
|
||||||
|
}
|
||||||
|
class Flow{
|
||||||
|
String Name
|
||||||
|
CategoryType Category
|
||||||
|
FlowType Type
|
||||||
|
FlowParameters Parameters
|
||||||
|
String Description
|
||||||
|
FlowType? AcceptedInput
|
||||||
|
OutputType[]? Outputs
|
||||||
|
Flow[]? ChildrenFlow
|
||||||
|
Flow? ParentFlow
|
||||||
|
Output? Output : Signal|Text|Candles
|
||||||
|
MapInput(AcceptedInput, ParentFlow.Output)
|
||||||
|
Run(ParentFlow.Output)
|
||||||
|
LoadChildren()
|
||||||
|
ExecuteChildren()
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
25
assets/documentation/PositionWorkflow.md
Normal file
25
assets/documentation/PositionWorkflow.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
subgraph Position
|
||||||
|
A(New)
|
||||||
|
F
|
||||||
|
G(Filled) --> L(Flipped)
|
||||||
|
G --> H(Finished)
|
||||||
|
A --> I(Cancelled)
|
||||||
|
A --> J(Rejected)
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Trade Open
|
||||||
|
A --> N
|
||||||
|
N(Requested) --> O(Filled)
|
||||||
|
O --> F(Partilly Filled)
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Trades SL/TP
|
||||||
|
F --> B(PendingOpen)
|
||||||
|
B -->C(Requested)
|
||||||
|
C -->P(Cancelled)
|
||||||
|
C -->D[Filled]
|
||||||
|
D --> G
|
||||||
|
end
|
||||||
|
```
|
||||||
BIN
assets/img/Architecture.png
Normal file
BIN
assets/img/Architecture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 199 KiB |
BIN
assets/img/doc-docker.png
Normal file
BIN
assets/img/doc-docker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
assets/img/doc-influxdb-apikeys-1.png
Normal file
BIN
assets/img/doc-influxdb-apikeys-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/img/doc-influxdb-apikeys-2.png
Normal file
BIN
assets/img/doc-influxdb-apikeys-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/img/doc-influxdb.png
Normal file
BIN
assets/img/doc-influxdb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/img/doc-settings.png
Normal file
BIN
assets/img/doc-settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
4
captain-definition
Normal file
4
captain-definition
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 2,
|
||||||
|
"dockerfilePath": "src/Managing.Api/Dockerfil"
|
||||||
|
}
|
||||||
13
scripts/build_and_run.sh
Normal file
13
scripts/build_and_run.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Navigate to the src directory
|
||||||
|
cd ../src
|
||||||
|
|
||||||
|
# Build the managing.api image
|
||||||
|
docker build -t managing.api -f Managing.Api/Dockerfile . --no-cache
|
||||||
|
|
||||||
|
# Build the managing.api.workers image
|
||||||
|
docker build -t managing.api.workers -f Managing.Api.Workers/Dockerfile . --no-cache
|
||||||
|
|
||||||
|
# Start up the project using docker-compose
|
||||||
|
docker-compose -f Managing.Docker/docker-compose.yml -f Managing.Docker/docker-compose.sandbox.yml up -d
|
||||||
4
scripts/clean-front-end-code.cmd
Normal file
4
scripts/clean-front-end-code.cmd
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
cd ..
|
||||||
|
cd .\src\Managing.WebApp\
|
||||||
|
npm run lint:fix
|
||||||
|
npm run prettier
|
||||||
5
scripts/docker-deploy-sandbox - Copy.cmd
Normal file
5
scripts/docker-deploy-sandbox - Copy.cmd
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
cd ..
|
||||||
|
cd .\src\
|
||||||
|
docker build -t managing.api -f ./Managing.Api/Dockerfile . --no-cache
|
||||||
|
docker build -t managing.api.workers -f ./Managing.Api.Workers/Dockerfile . --no-cache
|
||||||
|
docker-compose -f ./Managing.Docker/docker-compose.yml -f ./Managing.Docker/docker-compose.sandbox.yml up -d
|
||||||
5
scripts/docker-deploy-sandbox.cmd
Normal file
5
scripts/docker-deploy-sandbox.cmd
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
cd ..
|
||||||
|
cd .\src\
|
||||||
|
docker build -t managing.api -f ./Managing.Api/Dockerfile . --no-cache
|
||||||
|
docker build -t managing.api.workers -f ./Managing.Api.Workers/Dockerfile . --no-cache
|
||||||
|
docker-compose -f ./Managing.Docker/docker-compose.yml -f ./Managing.Docker/docker-compose.sandbox.yml up -d
|
||||||
22
scripts/docker-redeploy-oda.cmd
Normal file
22
scripts/docker-redeploy-oda.cmd
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
cd ..
|
||||||
|
cd .\src\
|
||||||
|
ECHO "Stopping containers..."
|
||||||
|
docker stop sandbox-managing.api-1
|
||||||
|
docker stop sandbox-managing.api.workers-1
|
||||||
|
ECHO "Contaiters stopped"
|
||||||
|
ECHO "Removing containers..."
|
||||||
|
docker rm sandbox-managing.api-1
|
||||||
|
docker rm sandbox-managing.api.workers-1
|
||||||
|
ECHO "Containers removed"
|
||||||
|
ECHO "Removing images..."
|
||||||
|
docker rmi managing.api
|
||||||
|
docker rmi managing.api:latest
|
||||||
|
docker rmi managing.api.workers
|
||||||
|
docker rmi managing.api.workers:latest
|
||||||
|
ECHO "Images removed"
|
||||||
|
ECHO "Building images..."
|
||||||
|
docker build -t managing.api -f ./Managing.Api/Dockerfile . --no-cache
|
||||||
|
docker build -t managing.api.workers -f ./Managing.Api.Workers/Dockerfile . --no-cache
|
||||||
|
ECHO "Deploying..."
|
||||||
|
docker-compose -f ./Managing.Docker/docker-compose.yml -f ./Managing.Docker/docker-compose.sandbox.yml up -d
|
||||||
|
ECHO "Deployed"
|
||||||
1
src/.dockerignore
Normal file
1
src/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
4
src/.editorconfig
Normal file
4
src/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# IDE0008: Use explicit type
|
||||||
|
dotnet_diagnostic.IDE0008.severity = none
|
||||||
45
src/Managing.Api.Workers.csproj
Normal file
45
src/Managing.Api.Workers.csproj
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
|
<UserSecretsId>3900ce93-de15-49e5-9a61-7dc2209939ca</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<DockerfileContext>..\..</DockerfileContext>
|
||||||
|
<DockerComposeProjectPath>..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Essential.LoggerProvider.Elasticsearch" Version="1.3.2" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||||
|
<PackageReference Include="NSwag.AspNetCore" Version="13.20.0" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Managing.Bootstrap\Managing.Bootstrap.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.Lowpro.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="appsettings.Oda-sandbox.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
31
src/Managing.Api.Workers/Controllers/WorkerController.cs
Normal file
31
src/Managing.Api.Workers/Controllers/WorkerController.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using Managing.Domain.Workers;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class WorkerController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IWorkerService _workerService;
|
||||||
|
|
||||||
|
public WorkerController(IWorkerService workerService)
|
||||||
|
{
|
||||||
|
_workerService = workerService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<List<Worker>> GetWorkers()
|
||||||
|
{
|
||||||
|
return Ok(_workerService.GetWorkers());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPatch]
|
||||||
|
public async Task<ActionResult> ToggleWorker(WorkerType workerType)
|
||||||
|
{
|
||||||
|
return Ok(await _workerService.ToggleWorker(workerType));
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/Managing.Api.Workers/Dockerfile
Normal file
36
src/Managing.Api.Workers/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Use the official Microsoft ASP.NET Core runtime as the base image.
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
EXPOSE 443
|
||||||
|
|
||||||
|
# Use the official Microsoft .NET SDK image to build the code.
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["Managing.Api.Workers/Managing.Api.Workers.csproj", "Managing.Api.Workers/"]
|
||||||
|
COPY ["Managing.Bootstrap/Managing.Bootstrap.csproj", "Managing.Bootstrap/"]
|
||||||
|
COPY ["Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj", "Managing.Infrastructure.Storage/"]
|
||||||
|
COPY ["Managing.Application/Managing.Application.csproj", "Managing.Application/"]
|
||||||
|
COPY ["Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj", "Managing.Infrastructure.MongoDb/"]
|
||||||
|
COPY ["Managing.Common/Managing.Common.csproj", "Managing.Common/"]
|
||||||
|
COPY ["Managing.Core/Managing.Core.csproj", "Managing.Core/"]
|
||||||
|
COPY ["Managing.Application.Abstractions/Managing.Application.Abstractions.csproj", "Managing.Application.Abstractions/"]
|
||||||
|
COPY ["Managing.Domain/Managing.Domain.csproj", "Managing.Domain/"]
|
||||||
|
COPY ["Managing.Application.Workers/Managing.Application.Workers.csproj", "Managing.Application.Workers/"]
|
||||||
|
COPY ["Managing.Infrastructure.Messengers/Managing.Infrastructure.Messengers.csproj", "Managing.Infrastructure.Messengers/"]
|
||||||
|
COPY ["Managing.Infrastructure.Exchanges/Managing.Infrastructure.Exchanges.csproj", "Managing.Infrastructure.Exchanges/"]
|
||||||
|
COPY ["Managing.Infrastructure.Database/Managing.Infrastructure.Databases.csproj", "Managing.Infrastructure.Database/"]
|
||||||
|
RUN dotnet restore "Managing.Api.Workers/Managing.Api.Workers.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/Managing.Api.Workers"
|
||||||
|
RUN dotnet build "Managing.Api.Workers.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "Managing.Api.Workers.csproj" -c Release -o /app/publish
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
COPY Managing.Api.Workers/managing_cert.pfx .
|
||||||
|
COPY Managing.Api.Workers/appsettings.Lowpro.json ./appsettings.json
|
||||||
|
ENTRYPOINT ["dotnet", "Managing.Api.Workers.dll"]
|
||||||
20
src/Managing.Api.Workers/Filters/EnumSchemaFilter.cs
Normal file
20
src/Managing.Api.Workers/Filters/EnumSchemaFilter.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.OpenApi.Any;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Filters
|
||||||
|
{
|
||||||
|
public class EnumSchemaFilter : ISchemaFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
||||||
|
{
|
||||||
|
if (context.Type.IsEnum)
|
||||||
|
{
|
||||||
|
model.Enum.Clear();
|
||||||
|
Enum.GetNames(context.Type)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(n => model.Enum.Add(new OpenApiString(n)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Managing.Api.Workers/Managing.Api.Workers.csproj
Normal file
45
src/Managing.Api.Workers/Managing.Api.Workers.csproj
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
|
<UserSecretsId>3900ce93-de15-49e5-9a61-7dc2209939ca</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<DockerfileContext>..\..</DockerfileContext>
|
||||||
|
<DockerComposeProjectPath>..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Essential.LoggerProvider.Elasticsearch" Version="1.3.2" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||||
|
<PackageReference Include="NSwag.AspNetCore" Version="13.20.0" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Managing.Bootstrap\Managing.Bootstrap.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.Lowpro.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="appsettings.Oda-docker.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
150
src/Managing.Api.Workers/Program.cs
Normal file
150
src/Managing.Api.Workers/Program.cs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Managing.Api.Workers.Filters;
|
||||||
|
using Managing.Api.Workers.Workers;
|
||||||
|
using Managing.Api.WorkersExceptions;
|
||||||
|
using Managing.Application.Hubs;
|
||||||
|
using Managing.Bootstrap;
|
||||||
|
using Managing.Common;
|
||||||
|
using Managing.Infrastructure.Databases.InfluxDb.Models;
|
||||||
|
using Managing.Infrastructure.Databases.MongoDb;
|
||||||
|
using NSwag;
|
||||||
|
using NSwag.Generation.Processors.Security;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Sinks.Elasticsearch;
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
builder.Configuration
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
|
||||||
|
builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
|
||||||
|
{
|
||||||
|
var envName = builder.Environment.EnvironmentName.ToLower().Replace(".", "-");
|
||||||
|
var indexFormat = $"managing-worker-{envName}-" + "{0:yyyy.MM.dd}";
|
||||||
|
var yourTemplateName = "dotnetlogs";
|
||||||
|
var es = new ElasticsearchSinkOptions(new Uri(hostBuilder.Configuration["ElasticConfiguration:Uri"]))
|
||||||
|
{
|
||||||
|
IndexFormat = indexFormat.ToLower(),
|
||||||
|
AutoRegisterTemplate = true,
|
||||||
|
OverwriteTemplate = true,
|
||||||
|
TemplateName = yourTemplateName,
|
||||||
|
AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
|
||||||
|
TypeName = null,
|
||||||
|
BatchAction = ElasticOpType.Create,
|
||||||
|
MinimumLogEventLevel = Serilog.Events.LogEventLevel.Information,
|
||||||
|
FailureCallback = e => Console.WriteLine($"Unable to submit event {e.RenderMessage()} to ElasticSearch. " +
|
||||||
|
$"Full message : " + e.Exception.Message),
|
||||||
|
DetectElasticsearchVersion = true,
|
||||||
|
RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
|
||||||
|
};
|
||||||
|
|
||||||
|
loggerConfiguration
|
||||||
|
.WriteTo.Console()
|
||||||
|
.WriteTo.Elasticsearch(es);
|
||||||
|
});
|
||||||
|
builder.Services.AddOptions();
|
||||||
|
builder.Services.Configure<ManagingDatabaseSettings>(builder.Configuration.GetSection(Constants.Databases.MongoDb));
|
||||||
|
builder.Services.Configure<InfluxDbSettings>(builder.Configuration.GetSection(Constants.Databases.InfluxDb));
|
||||||
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
|
||||||
|
builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.SetIsOriginAllowed((host) => true)
|
||||||
|
.AllowAnyOrigin()
|
||||||
|
.WithOrigins("http://localhost:3000/")
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials();
|
||||||
|
}));
|
||||||
|
builder.Services.AddSignalR().AddJsonProtocol();
|
||||||
|
|
||||||
|
builder.Services.RegisterWorkersDependencies(builder.Configuration);
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddOpenApiDocument(document =>
|
||||||
|
{
|
||||||
|
document.AddSecurity("JWT", Enumerable.Empty<string>(), new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Type = OpenApiSecuritySchemeType.ApiKey,
|
||||||
|
Name = "Authorization",
|
||||||
|
In = OpenApiSecurityApiKeyLocation.Header,
|
||||||
|
Description = "Type into the textbox: Bearer {your JWT token}."
|
||||||
|
});
|
||||||
|
|
||||||
|
document.OperationProcessors.Add(
|
||||||
|
new AspNetCoreOperationSecurityScopeProcessor("JWT"));
|
||||||
|
});
|
||||||
|
builder.Services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.SchemaFilter<EnumSchemaFilter>();
|
||||||
|
options.AddSecurityDefinition("Bearer,", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = "Please insert your JWT Token into field : Bearer {your_token}",
|
||||||
|
Name = "Authorization",
|
||||||
|
Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http,
|
||||||
|
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
|
||||||
|
Scheme = "Bearer",
|
||||||
|
BearerFormat = "JWT"
|
||||||
|
});
|
||||||
|
options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement{
|
||||||
|
{
|
||||||
|
new Microsoft.OpenApi.Models.OpenApiSecurityScheme{
|
||||||
|
Reference = new Microsoft.OpenApi.Models.OpenApiReference{
|
||||||
|
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
|
||||||
|
Id = "Bearer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new string[]{}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.WebHost.SetupDiscordBot();
|
||||||
|
builder.Services.AddHostedService<FeeWorker>();
|
||||||
|
builder.Services.AddHostedService<PositionManagerWorker>();
|
||||||
|
builder.Services.AddHostedService<PositionFetcher>();
|
||||||
|
builder.Services.AddHostedService<PricesFiveMinutesWorker>();
|
||||||
|
builder.Services.AddHostedService<PricesFifteenMinutesWorker>();
|
||||||
|
builder.Services.AddHostedService<PricesOneHourWorker>();
|
||||||
|
builder.Services.AddHostedService<PricesFourHoursWorker>();
|
||||||
|
builder.Services.AddHostedService<PricesOneDayWorker>();
|
||||||
|
builder.Services.AddHostedService<PositionManagerWorker>();
|
||||||
|
builder.Services.AddHostedService<SpotlightWorker>();
|
||||||
|
builder.Services.AddHostedService<TraderWatcher>();
|
||||||
|
builder.Services.AddHostedService<LeaderboardWorker>();
|
||||||
|
builder.Services.AddHostedService<NoobiesboardWorker>();
|
||||||
|
|
||||||
|
// App
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseSerilogRequestLogging();
|
||||||
|
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseOpenApi();
|
||||||
|
app.UseSwaggerUi3();
|
||||||
|
app.UseSwaggerUI(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing Workers v1");
|
||||||
|
c.RoutePrefix = string.Empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseCors("CorsPolicy");
|
||||||
|
|
||||||
|
app.UseMiddleware(typeof(GlobalErrorHandlingMiddleware));
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapControllers();
|
||||||
|
endpoints.MapHub<PositionHub>("/positionhub");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run();
|
||||||
75
src/Managing.Api.Workers/Workers/BaseWorker.cs
Normal file
75
src/Managing.Api.Workers/Workers/BaseWorker.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers;
|
||||||
|
|
||||||
|
public abstract class BaseWorker<T> : BackgroundService where T : class
|
||||||
|
{
|
||||||
|
private readonly WorkerType _workerType;
|
||||||
|
protected readonly ILogger<T> _logger;
|
||||||
|
protected readonly TimeSpan _delay;
|
||||||
|
private readonly IWorkerService _workerService;
|
||||||
|
private int _executionCount;
|
||||||
|
|
||||||
|
protected BaseWorker(
|
||||||
|
WorkerType workerType,
|
||||||
|
ILogger<T> logger,
|
||||||
|
TimeSpan timeSpan,
|
||||||
|
IWorkerService workerService)
|
||||||
|
{
|
||||||
|
_workerType = workerType;
|
||||||
|
_logger = logger;
|
||||||
|
_delay = timeSpan == TimeSpan.Zero ? TimeSpan.FromMinutes(1) : timeSpan;
|
||||||
|
_workerService = workerService;
|
||||||
|
_executionCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{_workerType}] Starting");
|
||||||
|
var worker = await _workerService.GetWorker(_workerType);
|
||||||
|
|
||||||
|
if (worker == null)
|
||||||
|
{
|
||||||
|
await _workerService.InsertWorker(_workerType, _delay);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{_workerType}] Last run : {worker.LastRunTime} - Execution Count : {worker.ExecutionCount}");
|
||||||
|
_executionCount = worker.ExecutionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.Register(() => _logger.LogInformation($"[{_workerType}] Stopping"));
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
worker = await _workerService.GetWorker(_workerType);
|
||||||
|
|
||||||
|
//if (true)
|
||||||
|
if (worker.IsActive)
|
||||||
|
{
|
||||||
|
await Run(cancellationToken);
|
||||||
|
_executionCount++;
|
||||||
|
await _workerService.UpdateWorker(_workerType, _executionCount);
|
||||||
|
_logger.LogInformation($"[{_workerType}] Run ok. Next run at : {DateTime.UtcNow.Add(_delay)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{_workerType}] Worker not active. Next run at : {DateTime.UtcNow.Add(_delay)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(_delay);
|
||||||
|
}
|
||||||
|
_logger.LogInformation($"[{_workerType}] Stopped");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"Error : {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Task Run(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
29
src/Managing.Api.Workers/Workers/FeeWorker.cs
Normal file
29
src/Managing.Api.Workers/Workers/FeeWorker.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class FeeWorker : BaseWorker<FeeWorker>
|
||||||
|
{
|
||||||
|
private readonly ITradingService _tradingService;
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.Fee;
|
||||||
|
|
||||||
|
public FeeWorker(
|
||||||
|
ILogger<FeeWorker> logger,
|
||||||
|
ITradingService tradingService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromHours(12),
|
||||||
|
workerService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_tradingService = tradingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_tradingService.UpdateFee(TradingExchanges.Evm);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Managing.Api.Workers/Workers/LeaderboardWorker.cs
Normal file
28
src/Managing.Api.Workers/Workers/LeaderboardWorker.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class LeaderboardWorker : BaseWorker<FeeWorker>
|
||||||
|
{
|
||||||
|
private readonly IStatisticService _statisticService;
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.LeaderboardWorker;
|
||||||
|
|
||||||
|
public LeaderboardWorker(
|
||||||
|
ILogger<FeeWorker> logger,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromHours(24),
|
||||||
|
workerService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_statisticService = statisticService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _statisticService.UpdateLeaderboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs
Normal file
28
src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class NoobiesboardWorker : BaseWorker<FeeWorker>
|
||||||
|
{
|
||||||
|
private readonly IStatisticService _statisticService;
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.Noobiesboard;
|
||||||
|
|
||||||
|
public NoobiesboardWorker(
|
||||||
|
ILogger<FeeWorker> logger,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromHours(24),
|
||||||
|
workerService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_statisticService = statisticService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _statisticService.UpdateNoobiesboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/Managing.Api.Workers/Workers/PositionFetcher.cs
Normal file
37
src/Managing.Api.Workers/Workers/PositionFetcher.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Hubs;
|
||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class PositionFetcher : BaseWorker<PositionFetcher>
|
||||||
|
{
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.PositionFetcher;
|
||||||
|
private readonly ITradingService _tradingService;
|
||||||
|
private readonly IHubContext<PositionHub> _hubContext;
|
||||||
|
private readonly ILogger<PositionFetcher> _logger;
|
||||||
|
|
||||||
|
public PositionFetcher(
|
||||||
|
ILogger<PositionFetcher> logger,
|
||||||
|
IWorkerService workerService,
|
||||||
|
ITradingService tradingService,
|
||||||
|
|
||||||
|
IHubContext<PositionHub> hubContext) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromSeconds(10),
|
||||||
|
workerService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_tradingService = tradingService;
|
||||||
|
_hubContext = hubContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var positions = _tradingService.GetPositions().Where(p => p.Initiator != PositionInitiator.PaperTrading);
|
||||||
|
await _hubContext.Clients.All.SendAsync("Positions", positions);
|
||||||
|
}
|
||||||
|
}
|
||||||
200
src/Managing.Api.Workers/Workers/PositionManagerWorker.cs
Normal file
200
src/Managing.Api.Workers/Workers/PositionManagerWorker.cs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using Managing.Domain.Trades;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class PositionManagerWorker : BaseWorker<PositionManagerWorker>
|
||||||
|
{
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.PositionManager;
|
||||||
|
private readonly ITradingService _tradingService;
|
||||||
|
private readonly IExchangeService _exchangeService;
|
||||||
|
private readonly IAccountService _accountService;
|
||||||
|
private readonly ILogger<PositionManagerWorker> _logger;
|
||||||
|
|
||||||
|
public PositionManagerWorker(
|
||||||
|
ILogger<PositionManagerWorker> logger,
|
||||||
|
IWorkerService workerService,
|
||||||
|
ITradingService tradingService,
|
||||||
|
IExchangeService exchangeService,
|
||||||
|
IAccountService accountService) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromMinutes(1),
|
||||||
|
workerService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_tradingService = tradingService;
|
||||||
|
_exchangeService = exchangeService;
|
||||||
|
_accountService = accountService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await ManageNewPositions();
|
||||||
|
await ManagePartillyFilledPositions();
|
||||||
|
await ManageFilledPositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ManagePartillyFilledPositions()
|
||||||
|
{
|
||||||
|
var positions = GetPositions(PositionStatus.PartiallyFilled);
|
||||||
|
|
||||||
|
_logger.LogInformation("Partilly filled positions count : {0} ", positions.Count());
|
||||||
|
|
||||||
|
foreach (var position in positions)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Managing Partilly filled position: {0} - Date: {1} - Direction: {2} - Ticker: {3}", position.Identifier, position.Date, position.OriginDirection, position.Ticker);
|
||||||
|
|
||||||
|
var account = await _accountService.GetAccount(position.AccountName, false, false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
if (position.StopLoss.Status == TradeStatus.PendingOpen)
|
||||||
|
{
|
||||||
|
var stopLoss = _exchangeService.OpenStopLoss(account, position.Ticker, position.OriginDirection, position.StopLoss.Price, position.StopLoss.Quantity, false, DateTime.UtcNow).Result;
|
||||||
|
|
||||||
|
if (stopLoss != null & (stopLoss.Status == TradeStatus.Requested))
|
||||||
|
{
|
||||||
|
position.StopLoss = stopLoss;
|
||||||
|
_logger.LogInformation("|_ SL is requested");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Stop loss not requested");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"|_ SL is already handle. Current status = {position.StopLoss.Status}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.TakeProfit1.Status == TradeStatus.PendingOpen)
|
||||||
|
{
|
||||||
|
var takeProfit1 = _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection, position.TakeProfit1.Price, position.TakeProfit1.Quantity, false, DateTime.UtcNow).Result;
|
||||||
|
|
||||||
|
if (takeProfit1 != null & (takeProfit1.Status == TradeStatus.Requested))
|
||||||
|
{
|
||||||
|
position.TakeProfit1 = takeProfit1;
|
||||||
|
_logger.LogInformation("|_ TP is requested");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Take Profit 1 not requested");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"|_ TP is already handle. Current status = {position.TakeProfit1.Status}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.TakeProfit2 != null &&
|
||||||
|
position.TakeProfit2.Status == TradeStatus.PendingOpen)
|
||||||
|
{
|
||||||
|
var takeProfit2 = _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection, position.TakeProfit2.Price, position.TakeProfit2.Quantity, false, DateTime.UtcNow).Result;
|
||||||
|
|
||||||
|
if (takeProfit2 != null & (takeProfit2.Status == TradeStatus.Requested))
|
||||||
|
{
|
||||||
|
position.TakeProfit2 = takeProfit2;
|
||||||
|
_logger.LogInformation("|_ TP2 is requested");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Take Profit 2 not requested");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("|_ TP2 is already handle or not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"|_ Cannot fully filled position because : {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.StopLoss.Status == TradeStatus.Requested
|
||||||
|
&& position.TakeProfit1.Status == TradeStatus.Requested
|
||||||
|
&& (position.TakeProfit2 == null || position.TakeProfit2.Status == TradeStatus.Requested))
|
||||||
|
{
|
||||||
|
position.Status = PositionStatus.Filled;
|
||||||
|
_logger.LogInformation($"|_ Position is now open and SL/TP are correctly requested");
|
||||||
|
}
|
||||||
|
|
||||||
|
_tradingService.UpdatePosition(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ManageFilledPositions()
|
||||||
|
{
|
||||||
|
var positions = GetPositions(PositionStatus.Filled);
|
||||||
|
|
||||||
|
_logger.LogInformation("Filled positions count : {0} ", positions.Count());
|
||||||
|
|
||||||
|
foreach (var position in positions)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Managing filled position: {0} - Date: {1} - Direction: {2} - Ticker: {3}", position.Identifier, position.Date, position.OriginDirection, position.Ticker);
|
||||||
|
var account = await _accountService.GetAccount(position.AccountName, false, false);
|
||||||
|
|
||||||
|
var updatedPosition = await _tradingService.ManagePosition(account, position);
|
||||||
|
_tradingService.UpdatePosition(updatedPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Position> GetPositions(PositionStatus positionStatus)
|
||||||
|
{
|
||||||
|
return _tradingService.GetPositionsByStatus(positionStatus)
|
||||||
|
.Where(p => p.Initiator != PositionInitiator.PaperTrading);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ManageNewPositions()
|
||||||
|
{
|
||||||
|
var positions = GetPositions(PositionStatus.New);
|
||||||
|
|
||||||
|
_logger.LogInformation("New positions count : {0} ", positions.Count());
|
||||||
|
|
||||||
|
foreach (var position in positions)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Managing position: {0} - Date: {1} - Direction: {2} - Ticker: {3}", position.Identifier, position.Date, position.OriginDirection, position.Ticker);
|
||||||
|
position.Status = PositionStatus.Updating;
|
||||||
|
_tradingService.UpdatePosition(position);
|
||||||
|
|
||||||
|
// Update status if position is open since to long
|
||||||
|
if (position.Date < DateTime.UtcNow.AddDays(-2))
|
||||||
|
{
|
||||||
|
position.Status = PositionStatus.Canceled;
|
||||||
|
_tradingService.UpdatePosition(position);
|
||||||
|
_logger.LogInformation($"|_ Position is now Canceled");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var account = await _accountService.GetAccount(position.AccountName, false, false);
|
||||||
|
if (!(await _exchangeService.GetOpenOrders(account, position.Ticker)).Any())
|
||||||
|
{
|
||||||
|
position.Status = PositionStatus.Canceled;
|
||||||
|
_tradingService.UpdatePosition(position);
|
||||||
|
_logger.LogInformation($"|_ Position is now Canceled - Position close from exchange");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var quantityInPosition = await _exchangeService.GetQuantityInPosition(account, position.Ticker);
|
||||||
|
|
||||||
|
if (quantityInPosition <= 0)
|
||||||
|
{
|
||||||
|
position.Status = PositionStatus.New;
|
||||||
|
_logger.LogInformation("|_ Position is currently waiting for filling");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
position.Open.SetStatus(TradeStatus.Filled);
|
||||||
|
position.Status = PositionStatus.PartiallyFilled;
|
||||||
|
_logger.LogInformation($"|_ Position is now PartiallyFilled");
|
||||||
|
}
|
||||||
|
|
||||||
|
_tradingService.UpdatePosition(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
40
src/Managing.Api.Workers/Workers/PricesBaseWorker.cs
Normal file
40
src/Managing.Api.Workers/Workers/PricesBaseWorker.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public abstract class PricesBaseWorker<T> : BaseWorker<T> where T : class
|
||||||
|
{
|
||||||
|
private readonly IPricesService _pricesService;
|
||||||
|
private readonly IStatisticService _statisticService;
|
||||||
|
private readonly Timeframe _timeframe;
|
||||||
|
|
||||||
|
public PricesBaseWorker(
|
||||||
|
ILogger<T> logger,
|
||||||
|
IPricesService pricesService,
|
||||||
|
IWorkerService workerService,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
TimeSpan delay,
|
||||||
|
WorkerType workerType,
|
||||||
|
Timeframe timeframe) : base(
|
||||||
|
workerType,
|
||||||
|
logger,
|
||||||
|
delay,
|
||||||
|
workerService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_pricesService = pricesService;
|
||||||
|
_statisticService = statisticService;
|
||||||
|
_timeframe = timeframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var tickers = _statisticService.GetTickers();
|
||||||
|
|
||||||
|
foreach (var ticker in tickers)
|
||||||
|
{
|
||||||
|
await _pricesService.UpdatePrice(TradingExchanges.Evm, ticker, _timeframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class PricesFifteenMinutesWorker : PricesBaseWorker<PricesFifteenMinutesWorker>
|
||||||
|
{
|
||||||
|
public PricesFifteenMinutesWorker(
|
||||||
|
ILogger<PricesFifteenMinutesWorker> logger,
|
||||||
|
IPricesService pricesService,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
logger,
|
||||||
|
pricesService,
|
||||||
|
workerService,
|
||||||
|
statisticService,
|
||||||
|
TimeSpan.FromMinutes(1),
|
||||||
|
WorkerType.PriceFifteenMinutes,
|
||||||
|
Timeframe.FifteenMinutes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Managing.Api.Workers/Workers/PricesFiveMinutesWorker.cs
Normal file
23
src/Managing.Api.Workers/Workers/PricesFiveMinutesWorker.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class PricesFiveMinutesWorker : PricesBaseWorker<PricesFiveMinutesWorker>
|
||||||
|
{
|
||||||
|
public PricesFiveMinutesWorker(
|
||||||
|
ILogger<PricesFiveMinutesWorker> logger,
|
||||||
|
IPricesService pricesService,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
logger,
|
||||||
|
pricesService,
|
||||||
|
workerService,
|
||||||
|
statisticService,
|
||||||
|
TimeSpan.FromMinutes(2.5),
|
||||||
|
WorkerType.PriceFiveMinutes,
|
||||||
|
Timeframe.FiveMinutes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Managing.Api.Workers/Workers/PricesFourHoursWorker.cs
Normal file
23
src/Managing.Api.Workers/Workers/PricesFourHoursWorker.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class PricesFourHoursWorker : PricesBaseWorker<PricesFourHoursWorker>
|
||||||
|
{
|
||||||
|
public PricesFourHoursWorker(
|
||||||
|
ILogger<PricesFourHoursWorker> logger,
|
||||||
|
IPricesService pricesService,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
logger,
|
||||||
|
pricesService,
|
||||||
|
workerService,
|
||||||
|
statisticService,
|
||||||
|
TimeSpan.FromHours(2),
|
||||||
|
WorkerType.PriceFourHour,
|
||||||
|
Timeframe.FourHour
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Managing.Api.Workers/Workers/PricesOneDayWorker.cs
Normal file
23
src/Managing.Api.Workers/Workers/PricesOneDayWorker.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class PricesOneDayWorker : PricesBaseWorker<PricesOneDayWorker>
|
||||||
|
{
|
||||||
|
public PricesOneDayWorker(
|
||||||
|
ILogger<PricesOneDayWorker> logger,
|
||||||
|
IPricesService pricesService,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
logger,
|
||||||
|
pricesService,
|
||||||
|
workerService,
|
||||||
|
statisticService,
|
||||||
|
TimeSpan.FromHours(12),
|
||||||
|
WorkerType.PriceOneDay,
|
||||||
|
Timeframe.OneDay
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/Managing.Api.Workers/Workers/PricesOneHourWorker.cs
Normal file
22
src/Managing.Api.Workers/Workers/PricesOneHourWorker.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class PricesOneHourWorker : PricesBaseWorker<PricesOneHourWorker>
|
||||||
|
{
|
||||||
|
public PricesOneHourWorker(
|
||||||
|
ILogger<PricesOneHourWorker> logger,
|
||||||
|
IPricesService pricesService,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
logger,
|
||||||
|
pricesService,
|
||||||
|
workerService,
|
||||||
|
statisticService,
|
||||||
|
TimeSpan.FromMinutes(30),
|
||||||
|
WorkerType.PriceOneHour,
|
||||||
|
Timeframe.OneHour)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/Managing.Api.Workers/Workers/SpotlightWorker.cs
Normal file
34
src/Managing.Api.Workers/Workers/SpotlightWorker.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using Managing.Common;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class SpotlightWorker : BaseWorker<SpotlightWorker>
|
||||||
|
{
|
||||||
|
private readonly IStatisticService _statisticService;
|
||||||
|
|
||||||
|
public SpotlightWorker(
|
||||||
|
ILogger<SpotlightWorker> logger,
|
||||||
|
IWorkerService workerService,
|
||||||
|
IStatisticService statisticService) : base(
|
||||||
|
Enums.WorkerType.Spotlight,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromMinutes(5),
|
||||||
|
workerService)
|
||||||
|
{
|
||||||
|
_statisticService = statisticService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async override Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _statisticService.UpdateSpotlight();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError("Enable to update spotlight", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs
Normal file
28
src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class TopVolumeTickerWorker : BaseWorker<TopVolumeTickerWorker>
|
||||||
|
{
|
||||||
|
private readonly IStatisticService _statisticService;
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.TopVolumeTicker;
|
||||||
|
|
||||||
|
public TopVolumeTickerWorker(
|
||||||
|
ILogger<TopVolumeTickerWorker> logger,
|
||||||
|
IWorkerService workerService,
|
||||||
|
IStatisticService statisticService) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromHours(12),
|
||||||
|
workerService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_statisticService = statisticService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _statisticService.UpdateTopVolumeTicker(TradingExchanges.Evm, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/Managing.Api.Workers/Workers/TraderWatcher.cs
Normal file
29
src/Managing.Api.Workers/Workers/TraderWatcher.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Workers.Workers;
|
||||||
|
|
||||||
|
public class TraderWatcher : BaseWorker<FeeWorker>
|
||||||
|
{
|
||||||
|
private readonly ITradingService _tradingService;
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.TraderWatcher;
|
||||||
|
|
||||||
|
public TraderWatcher(
|
||||||
|
ILogger<FeeWorker> logger,
|
||||||
|
ITradingService tradingService,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromSeconds(120),
|
||||||
|
workerService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_tradingService = tradingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _tradingService.WatchTrader();
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Managing.Api.Workers/appsettings.Development.json
Normal file
23
src/Managing.Api.Workers/appsettings.Development.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://localhost:27017",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://localhost:8086/",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://localhost:9200"
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
24
src/Managing.Api.Workers/appsettings.Khalid.json
Normal file
24
src/Managing.Api.Workers/appsettings.Khalid.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://localhost:27017",
|
||||||
|
"DatabaseName": "ManagingDb",
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://localhost:8086/",
|
||||||
|
"Organization": "",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://localhost:9200"
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
39
src/Managing.Api.Workers/appsettings.Lowpro.json
Normal file
39
src/Managing.Api.Workers/appsettings.Lowpro.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://managingdb:27017",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://influxdb:8086",
|
||||||
|
"Token": "OPjdwQBmKr0zQecJ10IDQ4bt32oOJzmFp687QWWzbGeyH0R-gCA6HnXI_B0oQ_InPmSUXKFje8DSAUPbY0hn-w==",
|
||||||
|
"Organization": "managing-org"
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200"
|
||||||
|
},
|
||||||
|
"Discord": {
|
||||||
|
"ApplicationId": "966075382002516031",
|
||||||
|
"PublicKey": "63028f6bb740cd5d26ae0340b582dee2075624011b28757436255fc002ca8a7c",
|
||||||
|
"TokenId": "OTY2MDc1MzgyMDAyNTE2MDMx.Yl8dzw.xpeIAaMwGrwTNY4r9JYv0ebzb-U",
|
||||||
|
|
||||||
|
"SignalChannelId": 1134858150667898910,
|
||||||
|
"TradesChannelId": 1134858092530634864,
|
||||||
|
"TroublesChannelId": 1134858233031446671,
|
||||||
|
"CopyTradingChannelId": 1134857874896588881,
|
||||||
|
"RequestsChannelId": 1018589494968078356,
|
||||||
|
"LeaderboardChannelId": 1133169725237633095,
|
||||||
|
"NoobiesboardChannelId": 1133504653485690940,
|
||||||
|
"ButtonExpirationMinutes": 10
|
||||||
|
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
24
src/Managing.Api.Workers/appsettings.Oda-docker.json
Normal file
24
src/Managing.Api.Workers/appsettings.Oda-docker.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://managingdb:27017",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://influxdb:8086/",
|
||||||
|
"Organization": "",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200"
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
24
src/Managing.Api.Workers/appsettings.Oda.json
Normal file
24
src/Managing.Api.Workers/appsettings.Oda.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://localhost:27017",
|
||||||
|
"DatabaseName": "ManagingDb",
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://localhost:8086/",
|
||||||
|
"Organization": "",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://localhost:9200"
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
27
src/Managing.Api/Authorization/JwtMiddleware.cs
Normal file
27
src/Managing.Api/Authorization/JwtMiddleware.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
namespace Managing.Api.Authorization;
|
||||||
|
|
||||||
|
|
||||||
|
public class JwtMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public JwtMiddleware(RequestDelegate next, IConfiguration config)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context, IUserService userService, IJwtUtils jwtUtils)
|
||||||
|
{
|
||||||
|
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
||||||
|
var userId = jwtUtils.ValidateJwtToken(token);
|
||||||
|
if (userId != null)
|
||||||
|
{
|
||||||
|
// attach user to context on successful jwt validation
|
||||||
|
context.Items["User"] = await userService.GetUserByAddressAsync(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/Managing.Api/Authorization/JwtUtils.cs
Normal file
70
src/Managing.Api/Authorization/JwtUtils.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using Managing.Domain.Users;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Managing.Api.Authorization;
|
||||||
|
|
||||||
|
|
||||||
|
public interface IJwtUtils
|
||||||
|
{
|
||||||
|
public string GenerateJwtToken(User user, string publicAddress);
|
||||||
|
public string ValidateJwtToken(string token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JwtUtils : IJwtUtils
|
||||||
|
{
|
||||||
|
private readonly string _secret;
|
||||||
|
public JwtUtils(IConfiguration config)
|
||||||
|
{
|
||||||
|
_secret = config.GetValue<string>("Jwt:Secret");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateJwtToken(User user, string publicAddress)
|
||||||
|
{
|
||||||
|
// generate token that is valid for 15 minutes
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var key = Encoding.ASCII.GetBytes(_secret);
|
||||||
|
var tokenDescriptor = new SecurityTokenDescriptor
|
||||||
|
{
|
||||||
|
Subject = new ClaimsIdentity(new[] { new Claim("address", publicAddress) }),
|
||||||
|
Expires = DateTime.UtcNow.AddDays(15),
|
||||||
|
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||||
|
};
|
||||||
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||||
|
return tokenHandler.WriteToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidateJwtToken(string token)
|
||||||
|
{
|
||||||
|
if (token == null || string.IsNullOrEmpty(token))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var key = Encoding.ASCII.GetBytes(_secret);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
|
||||||
|
ClockSkew = TimeSpan.Zero
|
||||||
|
}, out SecurityToken validatedToken);
|
||||||
|
|
||||||
|
var jwtToken = (JwtSecurityToken)validatedToken;
|
||||||
|
var address = jwtToken.Claims.First(x => x.Type == "address").Value;
|
||||||
|
|
||||||
|
// return user id from JWT token if validation successful
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// return null if validation fails
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/Managing.Api/Controllers/AccountController.cs
Normal file
58
src/Managing.Api/Controllers/AccountController.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Domain.Accounts;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class AccountController : BaseController
|
||||||
|
{
|
||||||
|
private readonly IAccountService _AccountService;
|
||||||
|
|
||||||
|
public AccountController(
|
||||||
|
IAccountService AccountService,
|
||||||
|
IUserService userService)
|
||||||
|
: base(userService)
|
||||||
|
{
|
||||||
|
_AccountService = AccountService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<Account>> PostAccount(Account Account)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
return Ok(await _AccountService.CreateAccount(user, Account));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("accounts")]
|
||||||
|
public async Task<ActionResult<IEnumerable<Account>>> GetAccounts()
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
return Ok(_AccountService.GetAccountsByUser(user, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("balances")]
|
||||||
|
public async Task<ActionResult<IEnumerable<Account>>> GetAccountsBalances()
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
return Ok(_AccountService.GetAccountsBalancesByUser(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<Account>> GetAccount(string name)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
return Ok(await _AccountService.GetAccountByUser(user, name, true, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public ActionResult DeleteAccount(string name)
|
||||||
|
{
|
||||||
|
var user = GetUser().Result;
|
||||||
|
return Ok(_AccountService.DeleteAccount(user, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
133
src/Managing.Api/Controllers/BacktestController.cs
Normal file
133
src/Managing.Api/Controllers/BacktestController.cs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Hubs;
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
|
using Managing.Domain.MoneyManagements;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class BacktestController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IHubContext<BotHub> _hubContext;
|
||||||
|
private readonly IBacktester _backtester;
|
||||||
|
private readonly IScenarioService _scenarioService;
|
||||||
|
private readonly IAccountService _accountService;
|
||||||
|
private readonly IMoneyManagementService _moneyManagementService;
|
||||||
|
|
||||||
|
public BacktestController(
|
||||||
|
IHubContext<BotHub> hubContext,
|
||||||
|
IBacktester backtester,
|
||||||
|
IScenarioService scenarioService,
|
||||||
|
IAccountService accountService,
|
||||||
|
IMoneyManagementService moneyManagementService)
|
||||||
|
{
|
||||||
|
_hubContext = hubContext;
|
||||||
|
_backtester = backtester;
|
||||||
|
_scenarioService = scenarioService;
|
||||||
|
_accountService = accountService;
|
||||||
|
_moneyManagementService = moneyManagementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<IEnumerable<Backtest>> Backtests()
|
||||||
|
{
|
||||||
|
return Ok(_backtester.GetBacktests());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public ActionResult DeleteBacktest(string id)
|
||||||
|
{
|
||||||
|
return Ok(_backtester.DeleteBacktest(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
[Route("deleteAll")]
|
||||||
|
public ActionResult DeleteBacktests()
|
||||||
|
{
|
||||||
|
return Ok(_backtester.DeleteBacktests());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("Run")]
|
||||||
|
public async Task<ActionResult<Backtest>> Run(string accountName,
|
||||||
|
BotType botType,
|
||||||
|
Ticker ticker,
|
||||||
|
string scenarioName,
|
||||||
|
Timeframe timeframe,
|
||||||
|
bool watchOnly,
|
||||||
|
int days,
|
||||||
|
decimal balance,
|
||||||
|
string moneyManagementName,
|
||||||
|
MoneyManagement? moneyManagement = null,
|
||||||
|
bool save = false)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(accountName))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"'{nameof(accountName)}' cannot be null or empty.", nameof(accountName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(scenarioName))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"'{nameof(scenarioName)}' cannot be null or empty.", nameof(scenarioName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(moneyManagementName) && moneyManagement == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"'{nameof(moneyManagementName)}' and '{nameof(moneyManagement)}' cannot be null or empty.", nameof(moneyManagementName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (days > 0)
|
||||||
|
{
|
||||||
|
days = days * -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Backtest backtestResult = null;
|
||||||
|
var scenario = _scenarioService.GetScenario(scenarioName);
|
||||||
|
var account = await _accountService.GetAccount(accountName, true, false);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(moneyManagementName) && moneyManagement is null)
|
||||||
|
{
|
||||||
|
moneyManagement = await _moneyManagementService.GetMoneyMangement(moneyManagementName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
moneyManagement.FormatPercentage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scenario == null)
|
||||||
|
return BadRequest("No scenario found");
|
||||||
|
|
||||||
|
switch (botType)
|
||||||
|
{
|
||||||
|
case BotType.SimpleBot:
|
||||||
|
break;
|
||||||
|
case BotType.ScalpingBot:
|
||||||
|
backtestResult = _backtester.RunScalpingBotBacktest(account, moneyManagement, ticker, scenario,
|
||||||
|
timeframe, Convert.ToDouble(days), balance, watchOnly, save);
|
||||||
|
break;
|
||||||
|
case BotType.FlippingBot:
|
||||||
|
backtestResult = _backtester.RunFlippingBotBacktest(account, moneyManagement, ticker, scenario,
|
||||||
|
timeframe, Convert.ToDouble(days), balance, watchOnly, save);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await NotifyBacktesingSubscriberAsync(backtestResult);
|
||||||
|
|
||||||
|
return Ok(backtestResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task NotifyBacktesingSubscriberAsync(Backtest backtesting)
|
||||||
|
{
|
||||||
|
if(backtesting != null){
|
||||||
|
await _hubContext.Clients.All.SendAsync("BacktestsSubscription", backtesting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/Managing.Api/Controllers/BaseController.cs
Normal file
35
src/Managing.Api/Controllers/BaseController.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Domain.Users;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public abstract class BaseController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
|
public BaseController(IUserService userService)
|
||||||
|
{
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<User> GetUser()
|
||||||
|
{
|
||||||
|
var identity = HttpContext?.User.Identity as ClaimsIdentity;
|
||||||
|
if (identity != null)
|
||||||
|
{
|
||||||
|
var address = identity.Claims.FirstOrDefault(c => c.Type == "address").Value;
|
||||||
|
var user = await _userService.GetUserByAddressAsync(address);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
return user;
|
||||||
|
|
||||||
|
throw new Exception("User not found for this token");
|
||||||
|
}
|
||||||
|
throw new Exception("Not identity assigned to this token");
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/Managing.Api/Controllers/BotController.cs
Normal file
168
src/Managing.Api/Controllers/BotController.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
using Managing.Api.Models.Requests;
|
||||||
|
using Managing.Api.Models.Responses;
|
||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Application.Hubs;
|
||||||
|
using Managing.Application.ManageBot.Commands;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class BotController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
private readonly ILogger<BotController> _logger;
|
||||||
|
private readonly IHubContext<BotHub> _hubContext;
|
||||||
|
private readonly IBacktester _backtester;
|
||||||
|
|
||||||
|
public BotController(ILogger<BotController> logger, IMediator mediator, IHubContext<BotHub> hubContext, IBacktester backtester)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_mediator = mediator;
|
||||||
|
_hubContext = hubContext;
|
||||||
|
_backtester = backtester;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("Start")]
|
||||||
|
public async Task<ActionResult<string>> Start(StartBotRequest request)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new StartBotCommand(request.BotType, request.BotName, request.Ticker,
|
||||||
|
request.Scenario, request.Timeframe, request.AccountName, request.MoneyManagementName, request.IsForWatchOnly));
|
||||||
|
|
||||||
|
await NotifyBotSubscriberAsync();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Stop")]
|
||||||
|
public async Task<ActionResult<string>> Stop(BotType botType, string botName)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new StopBotCommand(botType, botName));
|
||||||
|
_logger.LogInformation($"{botType} type called {botName} is now {result}");
|
||||||
|
|
||||||
|
await NotifyBotSubscriberAsync();
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
[Route("Delete")]
|
||||||
|
public async Task<ActionResult<bool>> Delete(string botName)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new DeleteBotCommand(botName));
|
||||||
|
_logger.LogInformation($"{botName} is now deleted");
|
||||||
|
|
||||||
|
await NotifyBotSubscriberAsync();
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("StopAll")]
|
||||||
|
public async Task<string> StopAll()
|
||||||
|
{
|
||||||
|
var bots = await GetBotList();
|
||||||
|
var result = "";
|
||||||
|
foreach (var bot in bots)
|
||||||
|
{
|
||||||
|
result += $"{bot.Name} : ";
|
||||||
|
result = await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
|
||||||
|
result += $" |";
|
||||||
|
}
|
||||||
|
|
||||||
|
await NotifyBotSubscriberAsync();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Restart")]
|
||||||
|
public async Task<ActionResult<string>> Restart(BotType botType, string botName)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new RestartBotCommand(botType, botName));
|
||||||
|
_logger.LogInformation($"{botType} type called {botName} is now {result}");
|
||||||
|
|
||||||
|
await NotifyBotSubscriberAsync();
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("RestartAll")]
|
||||||
|
public async Task<string> RestartAll()
|
||||||
|
{
|
||||||
|
var bots = await GetBotList();
|
||||||
|
var result = "";
|
||||||
|
foreach (var bot in bots)
|
||||||
|
{
|
||||||
|
result += $"{bot.Name} : ";
|
||||||
|
result = await _mediator.Send(new RestartBotCommand(bot.BotType, bot.Name));
|
||||||
|
result += $" |";
|
||||||
|
}
|
||||||
|
|
||||||
|
await NotifyBotSubscriberAsync();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("ToggleIsForWatching")]
|
||||||
|
public async Task<ActionResult<string>> ToggleIsForWatching(string botName)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new ToggleIsForWatchingCommand(botName));
|
||||||
|
_logger.LogInformation($"{botName} bot is now {result}");
|
||||||
|
|
||||||
|
await NotifyBotSubscriberAsync();
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<List<TradingBot>> GetActiveBots()
|
||||||
|
{
|
||||||
|
return await GetBotList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<TradingBot>> GetBotList()
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new GetActiveBotsCommand());
|
||||||
|
var list = new List<TradingBot>();
|
||||||
|
|
||||||
|
foreach (var item in result)
|
||||||
|
{
|
||||||
|
list.Add(new TradingBot
|
||||||
|
{
|
||||||
|
Status = item.GetStatus(),
|
||||||
|
Name = item.GetName(),
|
||||||
|
Candles = item.Candles.ToList(),
|
||||||
|
Positions = item.Positions,
|
||||||
|
Signals = item.Signals.ToList(),
|
||||||
|
WinRate = item.GetWinRate(),
|
||||||
|
ProfitAndLoss = item.GetProfitAndLoss(),
|
||||||
|
Timeframe = item.Timeframe,
|
||||||
|
Ticker = item.Ticker,
|
||||||
|
AccountName = item.AccountName,
|
||||||
|
Scenario = item.Scenario,
|
||||||
|
IsForWatchingOnly = item.IsForWatchingOnly,
|
||||||
|
BotType = item.BotType,
|
||||||
|
MoneyManagement = item.MoneyManagement
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task NotifyBotSubscriberAsync()
|
||||||
|
{
|
||||||
|
var botsList = await GetBotList();
|
||||||
|
await _hubContext.Clients.All.SendAsync("BotsSubscription", botsList);
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/Managing.Api/Controllers/DataController.cs
Normal file
73
src/Managing.Api/Controllers/DataController.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Hubs;
|
||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using Managing.Domain.Candles;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class DataController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IExchangeService _exchangeService;
|
||||||
|
private readonly IAccountService _accountService;
|
||||||
|
private readonly ICacheService _cacheService;
|
||||||
|
private readonly IStatisticService _statisticService;
|
||||||
|
private readonly IHubContext<CandleHub> _hubContext;
|
||||||
|
|
||||||
|
public DataController(
|
||||||
|
IExchangeService exchangeService,
|
||||||
|
IAccountService accountService,
|
||||||
|
ICacheService cacheService,
|
||||||
|
IStatisticService statisticService,
|
||||||
|
IHubContext<CandleHub> hubContext)
|
||||||
|
{
|
||||||
|
_exchangeService = exchangeService;
|
||||||
|
_accountService = accountService;
|
||||||
|
_cacheService = cacheService;
|
||||||
|
_statisticService = statisticService;
|
||||||
|
_hubContext = hubContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("GetTickers")]
|
||||||
|
public async Task<ActionResult<Ticker[]>> GetTickers(string accountName, Timeframe timeframe)
|
||||||
|
{
|
||||||
|
var account = await _accountService.GetAccount(accountName, true, false);
|
||||||
|
var cacheKey = string.Concat(accountName, timeframe.ToString());
|
||||||
|
var tickers = _cacheService.GetOrSave(cacheKey, () =>
|
||||||
|
{
|
||||||
|
return _exchangeService.GetTickers(account, timeframe).Result;
|
||||||
|
}, TimeSpan.FromHours(2));
|
||||||
|
|
||||||
|
return Ok(tickers);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("Spotlight")]
|
||||||
|
public ActionResult<SpotlightOverview> GetSpotlight()
|
||||||
|
{
|
||||||
|
var overview = _cacheService.GetOrSave(nameof(SpotlightOverview), () =>
|
||||||
|
{
|
||||||
|
return _statisticService.GetLastSpotlight(DateTime.Now.AddHours(-3));
|
||||||
|
}, TimeSpan.FromMinutes(2));
|
||||||
|
|
||||||
|
if (overview?.Spotlights.Count < overview?.ScenarioCount)
|
||||||
|
{
|
||||||
|
overview = _statisticService.GetLastSpotlight(DateTime.Now.AddHours(-3));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(overview);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetCandles")]
|
||||||
|
public async Task<ActionResult<List<Candle>>> GetCandles(TradingExchanges exchange, Ticker ticker, DateTime startDate, Timeframe timeframe)
|
||||||
|
{
|
||||||
|
return Ok(await _exchangeService.GetCandlesInflux(exchange, ticker, startDate, timeframe));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
src/Managing.Api/Controllers/MoneyManagementController.cs
Normal file
44
src/Managing.Api/Controllers/MoneyManagementController.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Domain.MoneyManagements;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class MoneyManagementController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IMoneyManagementService _moneyManagementService;
|
||||||
|
public MoneyManagementController(IMoneyManagementService moneyManagementService)
|
||||||
|
{
|
||||||
|
_moneyManagementService = moneyManagementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<MoneyManagement>> PostMoneyManagement(MoneyManagement moneyManagement)
|
||||||
|
{
|
||||||
|
return Ok(await _moneyManagementService.CreateOrUpdateMoneyManagement(moneyManagement));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("moneymanagements")]
|
||||||
|
public ActionResult<IEnumerable<MoneyManagement>> GetMoneyManagements()
|
||||||
|
{
|
||||||
|
return Ok(_moneyManagementService.GetMoneyMangements());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<MoneyManagement> GetMoneyManagement(string name)
|
||||||
|
{
|
||||||
|
return Ok(_moneyManagementService.GetMoneyMangement(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public ActionResult DeleteMoneyManagement(string name)
|
||||||
|
{
|
||||||
|
return Ok(_moneyManagementService.DeleteMoneyManagement(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/Managing.Api/Controllers/ScenarioController.cs
Normal file
83
src/Managing.Api/Controllers/ScenarioController.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Domain.Scenarios;
|
||||||
|
using Managing.Domain.Strategies;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class ScenarioController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IScenarioService _scenarioService;
|
||||||
|
public ScenarioController(IScenarioService scenarioService)
|
||||||
|
{
|
||||||
|
_scenarioService = scenarioService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<IEnumerable<Scenario>> GetScenarios()
|
||||||
|
{
|
||||||
|
return Ok(_scenarioService.GetScenarios());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public ActionResult<Scenario> CreateScenario(string name, List<string> strategies)
|
||||||
|
{
|
||||||
|
return Ok(_scenarioService.CreateScenario(name, strategies));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public ActionResult DeleteScenario(string name)
|
||||||
|
{
|
||||||
|
return Ok(_scenarioService.DeleteScenario(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("strategy")]
|
||||||
|
public ActionResult<IEnumerable<Strategy>> GetStrategies()
|
||||||
|
{
|
||||||
|
return Ok(_scenarioService.GetStrategies());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("strategy")]
|
||||||
|
public ActionResult<Strategy> CreateStrategy(
|
||||||
|
StrategyType strategyType,
|
||||||
|
Timeframe timeframe,
|
||||||
|
string name,
|
||||||
|
int? period = null,
|
||||||
|
int? fastPeriods = null,
|
||||||
|
int? slowPeriods = null,
|
||||||
|
int? signalPeriods = null,
|
||||||
|
double? multiplier = null,
|
||||||
|
int? stochPeriods = null,
|
||||||
|
int? smoothPeriods = null,
|
||||||
|
int? cyclePeriods = null)
|
||||||
|
{
|
||||||
|
return Ok(_scenarioService.CreateStrategy(
|
||||||
|
strategyType,
|
||||||
|
timeframe,
|
||||||
|
name,
|
||||||
|
period,
|
||||||
|
fastPeriods,
|
||||||
|
slowPeriods,
|
||||||
|
signalPeriods,
|
||||||
|
multiplier,
|
||||||
|
stochPeriods,
|
||||||
|
smoothPeriods,
|
||||||
|
cyclePeriods));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
[Route("strategy")]
|
||||||
|
public ActionResult DeleteStrategy(string name)
|
||||||
|
{
|
||||||
|
return Ok(_scenarioService.DeleteStrategy(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/Managing.Api/Controllers/SettingsController.cs
Normal file
30
src/Managing.Api/Controllers/SettingsController.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class SettingsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
public SettingsController(ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
_settingsService = settingsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public ActionResult SetupSettings()
|
||||||
|
{
|
||||||
|
return Ok(_settingsService.SetupSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public async Task<ActionResult<bool>> ResetSettings()
|
||||||
|
{
|
||||||
|
return Ok(await _settingsService.ResetSettings());
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/Managing.Api/Controllers/TradingController.cs
Normal file
107
src/Managing.Api/Controllers/TradingController.cs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Trading.Commands;
|
||||||
|
using Managing.Domain.MoneyManagements;
|
||||||
|
using Managing.Domain.Trades;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class TradingController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ICommandHandler<OpenPositionRequest, Position> _openTradeCommandHandler;
|
||||||
|
private readonly ICommandHandler<ClosePositionCommand, Position> _closeTradeCommandHandler;
|
||||||
|
private readonly ITradingService _tradingService;
|
||||||
|
private readonly IMoneyManagementService _moneyManagementService;
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
|
private readonly ILogger<TradingController> _logger;
|
||||||
|
|
||||||
|
public TradingController(
|
||||||
|
ILogger<TradingController> logger,
|
||||||
|
ICommandHandler<OpenPositionRequest, Position> openTradeCommandHandler,
|
||||||
|
ICommandHandler<ClosePositionCommand, Position> closeTradeCommandHandler,
|
||||||
|
ITradingService tradingService,
|
||||||
|
IMediator mediator)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_openTradeCommandHandler = openTradeCommandHandler;
|
||||||
|
_closeTradeCommandHandler = closeTradeCommandHandler;
|
||||||
|
_tradingService = tradingService;
|
||||||
|
_mediator = mediator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetPositions")]
|
||||||
|
public async Task<ActionResult<List<Position>>> GetPositions(PositionInitiator positionInitiator)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new GetPositionsCommand(positionInitiator));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetTrade")]
|
||||||
|
public async Task<ActionResult<Trade>> GetTrade(string accountName, Ticker ticker, string exchangeOrderId)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new GetTradeCommand(accountName, exchangeOrderId, ticker));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetTrades")]
|
||||||
|
public async Task<ActionResult<Trade>> GetTrades(string accountName, Ticker ticker, string exchangeOrderId)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(new GetTradesCommand(ticker, accountName));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("ClosePosition")]
|
||||||
|
public async Task<ActionResult<Position>> ClosePosition(string identifier)
|
||||||
|
{
|
||||||
|
var position = _tradingService.GetPositionByIdentifier(identifier);
|
||||||
|
var result = await _closeTradeCommandHandler.Handle(new ClosePositionCommand(position));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("OpenPosition")]
|
||||||
|
public async Task<ActionResult<Position>> Trade(
|
||||||
|
string accountName,
|
||||||
|
string moneyManagementName,
|
||||||
|
TradeDirection direction,
|
||||||
|
Ticker ticker,
|
||||||
|
RiskLevel riskLevel,
|
||||||
|
bool isForPaperTrading,
|
||||||
|
MoneyManagement? moneyManagement = null,
|
||||||
|
decimal? openPrice = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(accountName))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"'{nameof(accountName)}' cannot be null or empty.", nameof(accountName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(moneyManagementName) && moneyManagement == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"'{nameof(moneyManagementName)}' cannot be null or empty.", nameof(moneyManagementName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moneyManagement == null)
|
||||||
|
{
|
||||||
|
moneyManagement = await _moneyManagementService.GetMoneyMangement(moneyManagementName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = new OpenPositionRequest(
|
||||||
|
accountName,
|
||||||
|
moneyManagement,
|
||||||
|
direction,
|
||||||
|
ticker,
|
||||||
|
PositionInitiator.User,
|
||||||
|
DateTime.UtcNow,
|
||||||
|
isForPaperTrading: isForPaperTrading,
|
||||||
|
price: openPrice);
|
||||||
|
var result = await _openTradeCommandHandler.Handle(command);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Managing.Api/Controllers/UserController.cs
Normal file
39
src/Managing.Api/Controllers/UserController.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Managing.Api.Authorization;
|
||||||
|
using Managing.Api.Models.Requests;
|
||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class UserController : ControllerBase
|
||||||
|
{
|
||||||
|
private IConfiguration _config;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
private readonly IJwtUtils _jwtUtils;
|
||||||
|
|
||||||
|
public UserController(IConfiguration config, IUserService userService, IJwtUtils jwtUtils)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
_userService = userService;
|
||||||
|
_jwtUtils = jwtUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<string>> CreateToken([FromBody] LoginRequest login)
|
||||||
|
{
|
||||||
|
var user = await _userService.Authenticate(login.Name, login.Address, login.Message, login.Signature);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
var tokenString = _jwtUtils.GenerateJwtToken(user, login.Address);
|
||||||
|
return Ok(tokenString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Managing.Api/Controllers/WorkflowController.cs
Normal file
45
src/Managing.Api/Controllers/WorkflowController.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Domain.Workflows;
|
||||||
|
using Managing.Domain.Workflows.Synthetics;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Managing.Api.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class WorkflowController : BaseController
|
||||||
|
{
|
||||||
|
private readonly IWorkflowService _workflowService;
|
||||||
|
|
||||||
|
public WorkflowController(IWorkflowService WorkflowService, IUserService userService) : base(userService)
|
||||||
|
{
|
||||||
|
_workflowService = WorkflowService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<Workflow>> PostWorkflow([ModelBinder]SyntheticWorkflow workflowRequest)
|
||||||
|
{
|
||||||
|
return Ok(await _workflowService.InsertOrUpdateWorkflow(workflowRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<IEnumerable<SyntheticWorkflow>> GetWorkflows()
|
||||||
|
{
|
||||||
|
return Ok(_workflowService.GetWorkflows());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("flows")]
|
||||||
|
public async Task<ActionResult<IEnumerable<IFlow>>> GetAvailableFlows()
|
||||||
|
{
|
||||||
|
return Ok(await _workflowService.GetAvailableFlows());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
public ActionResult DeleteWorkflow(string name)
|
||||||
|
{
|
||||||
|
return Ok(_workflowService.DeleteWorkflow(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/Managing.Api/Dockerfile
Normal file
36
src/Managing.Api/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Use the official Microsoft ASP.NET Core runtime as the base image.
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
EXPOSE 443
|
||||||
|
|
||||||
|
# Use the official Microsoft .NET SDK image to build the code.
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["Managing.Api/Managing.Api.csproj", "Managing.Api/"]
|
||||||
|
COPY ["Managing.Bootstrap/Managing.Bootstrap.csproj", "Managing.Bootstrap/"]
|
||||||
|
COPY ["Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj", "Managing.Infrastructure.Storage/"]
|
||||||
|
COPY ["Managing.Application/Managing.Application.csproj", "Managing.Application/"]
|
||||||
|
COPY ["Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj", "Managing.Infrastructure.MongoDb/"]
|
||||||
|
COPY ["Managing.Common/Managing.Common.csproj", "Managing.Common/"]
|
||||||
|
COPY ["Managing.Core/Managing.Core.csproj", "Managing.Core/"]
|
||||||
|
COPY ["Managing.Application.Abstractions/Managing.Application.Abstractions.csproj", "Managing.Application.Abstractions/"]
|
||||||
|
COPY ["Managing.Domain/Managing.Domain.csproj", "Managing.Domain/"]
|
||||||
|
COPY ["Managing.Application.Workers/Managing.Application.Workers.csproj", "Managing.Application.Workers/"]
|
||||||
|
COPY ["Managing.Infrastructure.Messengers/Managing.Infrastructure.Messengers.csproj", "Managing.Infrastructure.Messengers/"]
|
||||||
|
COPY ["Managing.Infrastructure.Exchanges/Managing.Infrastructure.Exchanges.csproj", "Managing.Infrastructure.Exchanges/"]
|
||||||
|
COPY ["Managing.Infrastructure.Database/Managing.Infrastructure.Databases.csproj", "Managing.Infrastructure.Database/"]
|
||||||
|
RUN dotnet restore "Managing.Api/Managing.Api.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/Managing.Api"
|
||||||
|
RUN dotnet build "Managing.Api.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "Managing.Api.csproj" -c Release -o /app/publish
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
COPY Managing.Api/managing_cert.pfx .
|
||||||
|
COPY Managing.Api/appsettings.Lowpro.json .
|
||||||
|
ENTRYPOINT ["dotnet", "Managing.Api.dll"]
|
||||||
62
src/Managing.Api/Exceptions/GlobalErrorHandlingMiddleware.cs
Normal file
62
src/Managing.Api/Exceptions/GlobalErrorHandlingMiddleware.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Managing.Api.Exceptions;
|
||||||
|
|
||||||
|
public class GlobalErrorHandlingMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
public GlobalErrorHandlingMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await HandleExceptionAsync(context, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
|
||||||
|
{
|
||||||
|
HttpStatusCode status;
|
||||||
|
var exceptionType = exception.GetType();
|
||||||
|
|
||||||
|
if (exceptionType == typeof(Exception))
|
||||||
|
{
|
||||||
|
status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
else if (exceptionType == typeof(NotImplementedException))
|
||||||
|
{
|
||||||
|
status = HttpStatusCode.NotImplemented;
|
||||||
|
}
|
||||||
|
else if (exceptionType == typeof(UnauthorizedAccessException))
|
||||||
|
{
|
||||||
|
status = HttpStatusCode.Unauthorized;
|
||||||
|
}
|
||||||
|
else if (exceptionType == typeof(ArgumentException))
|
||||||
|
{
|
||||||
|
status = HttpStatusCode.Unauthorized;
|
||||||
|
}
|
||||||
|
else if (exceptionType == typeof(KeyNotFoundException))
|
||||||
|
{
|
||||||
|
status = HttpStatusCode.Unauthorized;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = exception.Message;
|
||||||
|
var stackTrace = exception.StackTrace;
|
||||||
|
var exceptionResult = JsonSerializer.Serialize(new { error = message, stackTrace });
|
||||||
|
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
context.Response.StatusCode = (int)status;
|
||||||
|
return context.Response.WriteAsync(exceptionResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/Managing.Api/Filters/EnumSchemaFilter.cs
Normal file
20
src/Managing.Api/Filters/EnumSchemaFilter.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.OpenApi.Any;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace Managing.Api.Filters
|
||||||
|
{
|
||||||
|
public class EnumSchemaFilter : ISchemaFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiSchema model, SchemaFilterContext context)
|
||||||
|
{
|
||||||
|
if (context.Type.IsEnum)
|
||||||
|
{
|
||||||
|
model.Enum.Clear();
|
||||||
|
Enum.GetNames(context.Type)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(n => model.Enum.Add(new OpenApiString(n)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/Managing.Api/Managing.Api.csproj
Normal file
43
src/Managing.Api/Managing.Api.csproj
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
|
<DockerComposeProjectPath>..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||||
|
<UserSecretsId>7476db9f-ade4-414a-a420-e3ab55cb5f8d</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<DockerfileContext>..\..</DockerfileContext>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Essential.LoggerProvider.Elasticsearch" Version="1.3.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||||
|
<PackageReference Include="NSwag.AspNetCore" Version="13.20.0" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Managing.Bootstrap\Managing.Bootstrap.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="appsettings.Oda-sandbox.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
12
src/Managing.Api/Models/Requests/CreateScenarioRequest.cs
Normal file
12
src/Managing.Api/Models/Requests/CreateScenarioRequest.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Requests
|
||||||
|
{
|
||||||
|
public class CreateScenarioRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public List<string> Strategies { get; internal set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/Managing.Api/Models/Requests/CreateStrategyRequest.cs
Normal file
16
src/Managing.Api/Models/Requests/CreateStrategyRequest.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Requests;
|
||||||
|
|
||||||
|
public class CreateStrategyRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public StrategyType Type { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public Timeframe Timeframe { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public int Period { get; internal set; }
|
||||||
|
}
|
||||||
15
src/Managing.Api/Models/Requests/LoginRequest.cs
Normal file
15
src/Managing.Api/Models/Requests/LoginRequest.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Requests;
|
||||||
|
|
||||||
|
public class LoginRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Address { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Signature { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Message { get; set; }
|
||||||
|
}
|
||||||
15
src/Managing.Api/Models/Requests/RunBacktestRequest.cs
Normal file
15
src/Managing.Api/Models/Requests/RunBacktestRequest.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Requests
|
||||||
|
{
|
||||||
|
public class RunBacktestRequest
|
||||||
|
{
|
||||||
|
public TradingExchanges Exchange { get; set; }
|
||||||
|
public BotType BotType { get; set; }
|
||||||
|
public Ticker Ticker { get; set; }
|
||||||
|
public Timeframe Timeframe { get; set; }
|
||||||
|
public RiskLevel RiskLevel { get; set; }
|
||||||
|
public bool WatchOnly { get; set; }
|
||||||
|
public int Days { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/Managing.Api/Models/Requests/StartBotRequest.cs
Normal file
25
src/Managing.Api/Models/Requests/StartBotRequest.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Requests
|
||||||
|
{
|
||||||
|
public class StartBotRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public BotType BotType { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string BotName { get; set; }
|
||||||
|
[Required]
|
||||||
|
public Ticker Ticker { get; set; }
|
||||||
|
[Required]
|
||||||
|
public Timeframe Timeframe { get; set; }
|
||||||
|
[Required]
|
||||||
|
public bool IsForWatchOnly { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Scenario { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string AccountName { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string MoneyManagementName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Managing.Api/Models/Responses/TradingBot.cs
Normal file
45
src/Managing.Api/Models/Responses/TradingBot.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using Managing.Domain.Candles;
|
||||||
|
using Managing.Domain.MoneyManagements;
|
||||||
|
using Managing.Domain.Strategies;
|
||||||
|
using Managing.Domain.Trades;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Responses
|
||||||
|
{
|
||||||
|
public class TradingBot
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public string Status { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public List<Signal> Signals { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public List<Position> Positions { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public List<Candle> Candles { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public RiskLevel RiskLevel { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public int WinRate { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public decimal ProfitAndLoss { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public Timeframe Timeframe { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public Ticker Ticker { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public string Scenario { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public TradingExchanges Exchange { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public bool IsForWatchingOnly { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public BotType BotType { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public string AccountName { get; internal set; }
|
||||||
|
[Required]
|
||||||
|
public MoneyManagement MoneyManagement { get; internal set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/Managing.Api/Program.cs
Normal file
168
src/Managing.Api/Program.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Managing.Api.Authorization;
|
||||||
|
using Managing.Api.Exceptions;
|
||||||
|
using Managing.Api.Filters;
|
||||||
|
using Managing.Application.Hubs;
|
||||||
|
using Managing.Bootstrap;
|
||||||
|
using Managing.Common;
|
||||||
|
using Managing.Infrastructure.Databases.InfluxDb.Models;
|
||||||
|
using Managing.Infrastructure.Databases.MongoDb;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using NSwag;
|
||||||
|
using NSwag.Generation.Processors.Security;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Sinks.Elasticsearch;
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
builder.Configuration.AddJsonFile("appsettings.Lowpro.json", optional: true, reloadOnChange: true)
|
||||||
|
.AddJsonFile($"config.{builder.Environment.EnvironmentName}.json",
|
||||||
|
optional: true, reloadOnChange: true);
|
||||||
|
|
||||||
|
builder.Configuration.AddEnvironmentVariables();
|
||||||
|
builder.Configuration.AddUserSecrets<Program>();
|
||||||
|
builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
|
||||||
|
{
|
||||||
|
var envName = builder.Environment.EnvironmentName.ToLower().Replace(".", "-");
|
||||||
|
var indexFormat = $"managing-{envName}-" + "{0:yyyy.MM.dd}";
|
||||||
|
var yourTemplateName = "dotnetlogs";
|
||||||
|
var es = new ElasticsearchSinkOptions(new Uri(hostBuilder.Configuration["ElasticConfiguration:Uri"]))
|
||||||
|
{
|
||||||
|
IndexFormat = indexFormat.ToLower(),
|
||||||
|
AutoRegisterTemplate = true,
|
||||||
|
OverwriteTemplate = true,
|
||||||
|
TemplateName = yourTemplateName,
|
||||||
|
AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
|
||||||
|
TypeName = null,
|
||||||
|
BatchAction = ElasticOpType.Create,
|
||||||
|
MinimumLogEventLevel = Serilog.Events.LogEventLevel.Information,
|
||||||
|
FailureCallback = e => Console.WriteLine($"Unable to submit event {e.RenderMessage()} to ElasticSearch. " +
|
||||||
|
$"Full message : " + e.Exception.Message),
|
||||||
|
DetectElasticsearchVersion = true,
|
||||||
|
RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
|
||||||
|
};
|
||||||
|
|
||||||
|
loggerConfiguration
|
||||||
|
.WriteTo.Console()
|
||||||
|
.WriteTo.Elasticsearch(es);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddOptions();
|
||||||
|
builder.Services.Configure<ManagingDatabaseSettings>(builder.Configuration.GetSection(Constants.Databases.MongoDb));
|
||||||
|
builder.Services.Configure<InfluxDbSettings>(builder.Configuration.GetSection(Constants.Databases.InfluxDb));
|
||||||
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
|
||||||
|
|
||||||
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
|
||||||
|
{
|
||||||
|
o.SaveToken = true;
|
||||||
|
o.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidIssuer = builder.Configuration["Authentication:Schemes:Bearer:ValidIssuer"],
|
||||||
|
ValidAudience = builder.Configuration["Authentication:Schemes:Bearer:ValidAudiences"],
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey
|
||||||
|
(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])),
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
ValidateIssuerSigningKey = true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
builder.Services.AddAuthorization();
|
||||||
|
builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.SetIsOriginAllowed((host) => true)
|
||||||
|
.AllowAnyOrigin()
|
||||||
|
.WithOrigins("http://localhost:3000/")
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials();
|
||||||
|
}));
|
||||||
|
|
||||||
|
builder.Services.AddSignalR().AddJsonProtocol();
|
||||||
|
builder.Services.AddScoped<IJwtUtils, JwtUtils>();
|
||||||
|
|
||||||
|
builder.Services.RegisterApiDependencies(builder.Configuration);
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddOpenApiDocument(document =>
|
||||||
|
{
|
||||||
|
document.AddSecurity("JWT", Enumerable.Empty<string>(), new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Type = OpenApiSecuritySchemeType.ApiKey,
|
||||||
|
Name = "Authorization",
|
||||||
|
In = OpenApiSecurityApiKeyLocation.Header,
|
||||||
|
Description = "Type into the textbox: Bearer {your JWT token}."
|
||||||
|
});
|
||||||
|
|
||||||
|
document.OperationProcessors.Add(
|
||||||
|
new AspNetCoreOperationSecurityScopeProcessor("JWT"));
|
||||||
|
});
|
||||||
|
builder.Services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.SchemaFilter<EnumSchemaFilter>();
|
||||||
|
options.AddSecurityDefinition("Bearer,", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = "Please insert your JWT Token into field : Bearer {your_token}",
|
||||||
|
Name = "Authorization",
|
||||||
|
Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http,
|
||||||
|
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
|
||||||
|
Scheme = "Bearer",
|
||||||
|
BearerFormat = "JWT"
|
||||||
|
});
|
||||||
|
options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement{
|
||||||
|
{
|
||||||
|
new Microsoft.OpenApi.Models.OpenApiSecurityScheme{
|
||||||
|
Reference = new Microsoft.OpenApi.Models.OpenApiReference{
|
||||||
|
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
|
||||||
|
Id = "Bearer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new string[]{}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.WebHost.SetupDiscordBot();
|
||||||
|
|
||||||
|
// App
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseSerilogRequestLogging();
|
||||||
|
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseOpenApi();
|
||||||
|
app.UseSwaggerUi3();
|
||||||
|
app.UseSwaggerUI(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing API v1");
|
||||||
|
c.RoutePrefix = string.Empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseCors("CorsPolicy");
|
||||||
|
|
||||||
|
app.UseMiddleware(typeof(GlobalErrorHandlingMiddleware));
|
||||||
|
|
||||||
|
app.UseMiddleware<JwtMiddleware>();
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapControllers();
|
||||||
|
endpoints.MapHub<BotHub>("/bothub");
|
||||||
|
endpoints.MapHub<BacktestHub>("/backtesthub");
|
||||||
|
endpoints.MapHub<CandleHub>("/candlehub");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run();
|
||||||
13
src/Managing.Api/appsettings.Development.json
Normal file
13
src/Managing.Api/appsettings.Development.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Error",
|
||||||
|
"System": "Error",
|
||||||
|
"Microsoft": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200/"
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/Managing.Api/appsettings.Khalid.json
Normal file
34
src/Managing.Api/appsettings.Khalid.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://localhost:27017",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://localhost:8086/",
|
||||||
|
"Organization": "",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200"
|
||||||
|
},
|
||||||
|
"Discord": {
|
||||||
|
"ApplicationId": "",
|
||||||
|
"PublicKey": "",
|
||||||
|
"SignalChannelId": 1018897743118340180,
|
||||||
|
"TroublesChannelId": 1018897743118340180,
|
||||||
|
"TradesChannelId": 1020457417877753886,
|
||||||
|
"RequestChannelId": 1020463151034138694,
|
||||||
|
"RequestsChannelId": 1020463151034138694,
|
||||||
|
"ButtonExpirationMinutes": 2
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
46
src/Managing.Api/appsettings.Lowpro.json
Normal file
46
src/Managing.Api/appsettings.Lowpro.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"Authentication": {
|
||||||
|
"Schemes": {
|
||||||
|
"Bearer": {
|
||||||
|
"ValidAudiences": [ "http://localhost:3000/" ],
|
||||||
|
"ValidIssuer": "Managing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Jwt": {
|
||||||
|
"Secret": "2ed5f490-b6c1-4cad-8824-840c911f1fe6"
|
||||||
|
},
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://managingdb",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://influxdb:8086/",
|
||||||
|
"Organization": "managing-org",
|
||||||
|
"Token": "OPjdwQBmKr0zQecJ10IDQ4bt32oOJzmFp687QWWzbGeyH0R-gCA6HnXI_B0oQ_InPmSUXKFje8DSAUPbY0hn-w=="
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200"
|
||||||
|
},
|
||||||
|
"Discord": {
|
||||||
|
"ApplicationId": "966075382002516031",
|
||||||
|
"PublicKey": "63028f6bb740cd5d26ae0340b582dee2075624011b28757436255fc002ca8a7c",
|
||||||
|
"TokenId": "OTY2MDc1MzgyMDAyNTE2MDMx.Yl8dzw.xpeIAaMwGrwTNY4r9JYv0ebzb-U",
|
||||||
|
"SignalChannelId": 1134858150667898910,
|
||||||
|
"TradesChannelId": 1134858092530634864,
|
||||||
|
"TroublesChannelId": 1134858233031446671,
|
||||||
|
"CopyTradingChannelId": 1134857874896588881,
|
||||||
|
"RequestsChannelId": 1018589494968078356,
|
||||||
|
"ButtonExpirationMinutes": 10
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
37
src/Managing.Api/appsettings.Oda-Sandbox.json
Normal file
37
src/Managing.Api/appsettings.Oda-Sandbox.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://managingdb:27017",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://influxdb:8086/",
|
||||||
|
"Organization": "",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200"
|
||||||
|
},
|
||||||
|
"Discord": {
|
||||||
|
"BotActivity": "trading strategies",
|
||||||
|
"HandleUserAction": true,
|
||||||
|
"ApplicationId": "",
|
||||||
|
"PublicKey": "",
|
||||||
|
"TokenId": "",
|
||||||
|
"SignalChannelId": 966080506473099314,
|
||||||
|
"TradesChannelId": 998374177763491851,
|
||||||
|
"TroublesChannelId": 1015761955321040917,
|
||||||
|
"CopyTradingChannelId": 1132022887012909126,
|
||||||
|
"RequestsChannelId": 1018589494968078356,
|
||||||
|
"ButtonExpirationMinutes": 10
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
36
src/Managing.Api/appsettings.Oda-docker.json
Normal file
36
src/Managing.Api/appsettings.Oda-docker.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://managingdb:27017",
|
||||||
|
"DatabaseName": "ManagingDb",
|
||||||
|
"UserName": "admin",
|
||||||
|
"Password": "!MotdepasseFort11"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://influxdb:8086/",
|
||||||
|
"Organization": "managing-org",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200"
|
||||||
|
},
|
||||||
|
"Discord": {
|
||||||
|
"ApplicationId": "",
|
||||||
|
"PublicKey": "",
|
||||||
|
"TokenId": "",
|
||||||
|
"SignalChannelId": 966080506473099314,
|
||||||
|
"TradesChannelId": 998374177763491851,
|
||||||
|
"TroublesChannelId": 1015761955321040917,
|
||||||
|
"RequestsChannelId": 1018589494968078356,
|
||||||
|
"ButtonExpirationMinutes": 2
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
34
src/Managing.Api/appsettings.Oda.json
Normal file
34
src/Managing.Api/appsettings.Oda.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://localhost:27017",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://localhost:8086/",
|
||||||
|
"Organization": "",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200"
|
||||||
|
},
|
||||||
|
"Discord": {
|
||||||
|
"ApplicationId": "",
|
||||||
|
"PublicKey": "",
|
||||||
|
"TokenId": "",
|
||||||
|
"SignalChannelId": 966080506473099314,
|
||||||
|
"TradesChannelId": 998374177763491851,
|
||||||
|
"TroublesChannelId": 1015761955321040917,
|
||||||
|
"RequestsChannelId": 1018589494968078356,
|
||||||
|
"ButtonExpirationMinutes": 2
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
48
src/Managing.Api/appsettings.json
Normal file
48
src/Managing.Api/appsettings.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"Authentication": {
|
||||||
|
"Schemes": {
|
||||||
|
"Bearer": {
|
||||||
|
"ValidAudiences": [ "http://localhost:3000/" ],
|
||||||
|
"ValidIssuer": "Managing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Jwt": {
|
||||||
|
"Secret": "2ed5f490-b6c1-4cad-8824-840c911f1fe6"
|
||||||
|
},
|
||||||
|
"ManagingDatabase": {
|
||||||
|
"ConnectionString": "mongodb://managingdb",
|
||||||
|
"DatabaseName": "ManagingDb"
|
||||||
|
},
|
||||||
|
"InfluxDb": {
|
||||||
|
"Url": "http://influxdb:8086/",
|
||||||
|
"Organization": "",
|
||||||
|
"Token": ""
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ElasticConfiguration": {
|
||||||
|
"Uri": "http://elasticsearch:9200/"
|
||||||
|
},
|
||||||
|
"Discord": {
|
||||||
|
"BotActivity": "trading strategies",
|
||||||
|
"HandleUserAction": true,
|
||||||
|
"ApplicationId": "",
|
||||||
|
"PublicKey": "",
|
||||||
|
"TokenId": "",
|
||||||
|
"SignalChannelId": 966080506473099314,
|
||||||
|
"TradesChannelId": 998374177763491851,
|
||||||
|
"TroublesChannelId": 1015761955321040917,
|
||||||
|
"CopyTradingChannelId": 1132022887012909126,
|
||||||
|
"RequestsChannelId": 1018589494968078356,
|
||||||
|
"ButtonExpirationMinutes": 10
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
4
src/Managing.Api/captain-definition
Normal file
4
src/Managing.Api/captain-definition
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 2,
|
||||||
|
"dockerfilePath": "Dockerfile"
|
||||||
|
}
|
||||||
19
src/Managing.Api/failures.txt
Normal file
19
src/Managing.Api/failures.txt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{"Timestamp":"2022-09-13T02:59:02.0040536+02:00","Level":"Information","MessageTemplate":"02:59:01 Discord Discord.Net v3.8.0 (API v9)","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:02.0892523+02:00","Level":"Information","MessageTemplate":"02:59:02 Gateway Connecting","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:02.2504922+02:00","Level":"Information","MessageTemplate":"Now listening on: {address}","Properties":{"address":"https://localhost:5001","EventId":{"Id":14,"Name":"ListeningOnAddress"},"SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:02.2556804+02:00","Level":"Information","MessageTemplate":"Now listening on: {address}","Properties":{"address":"http://localhost:5000","EventId":{"Id":14,"Name":"ListeningOnAddress"},"SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:02.2591276+02:00","Level":"Information","MessageTemplate":"Application started. Press Ctrl+C to shut down.","Properties":{"SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:02.2625140+02:00","Level":"Information","MessageTemplate":"Hosting environment: {envName}","Properties":{"envName":"Development","SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:02.2651704+02:00","Level":"Information","MessageTemplate":"Content root path: {contentRoot}","Properties":{"contentRoot":"C:\\Users\\Utilisateur\\Desktop\\Projects\\apps\\Managing\\src\\Managing.Api\\","SourceContext":"Microsoft.Hosting.Lifetime","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:03.3572754+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway You're using the GuildScheduledEvents gateway intent without listening to any events related to that intent, consider removing the intent from your config.","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:03.3628433+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway You're using the GuildInvites gateway intent without listening to any events related to that intent, consider removing the intent from your config.","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:03.3688717+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway Connected","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:03.9425506+02:00","Level":"Information","MessageTemplate":"02:59:03 Gateway Ready","Properties":{"SourceContext":"Managing.Infrastructure.Messengers.Discord.DiscordService","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:10.5882097+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/index.html","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/index.html - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000001","RequestPath":"/index.html","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/index.html - -"}]}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:11.5711332+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":996.3451,"StatusCode":200,"ContentType":"text/html;charset=utf-8","ContentLength":null,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/index.html","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/index.html - - - 200 - text/html;charset=utf-8 996.3451ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000001","RequestPath":"/index.html","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/index.html - - - 200 - text/html;charset=utf-8 996.3451ms"}]}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:11.6123850+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_framework/aspnetcore-browser-refresh.js","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000003","RequestPath":"/_framework/aspnetcore-browser-refresh.js","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - -"}]}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:11.6281716+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":15.5581,"StatusCode":200,"ContentType":"application/javascript; charset=utf-8","ContentLength":11994,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_framework/aspnetcore-browser-refresh.js","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - - - 200 11994 application/javascript;+charset=utf-8 15.5581ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000003","RequestPath":"/_framework/aspnetcore-browser-refresh.js","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/_framework/aspnetcore-browser-refresh.js - - - 200 11994 application/javascript;+charset=utf-8 15.5581ms"}]}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:11.7015500+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_vs/browserLink","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/_vs/browserLink - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000005","RequestPath":"/_vs/browserLink","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/_vs/browserLink - -"}]}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:11.8214511+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":119.7799,"StatusCode":200,"ContentType":"text/javascript; charset=UTF-8","ContentLength":null,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/_vs/browserLink","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/_vs/browserLink - - - 200 - text/javascript;+charset=UTF-8 119.7799ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000005","RequestPath":"/_vs/browserLink","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/_vs/browserLink - - - 200 - text/javascript;+charset=UTF-8 119.7799ms"}]}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:11.9652804+02:00","Level":"Information","MessageTemplate":"{HostingRequestStartingLog:l}","Properties":{"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/swagger/v1/swagger.json","QueryString":"","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000007","RequestPath":"/swagger/v1/swagger.json","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestStartingLog":[{"Format":"l","Rendering":"Request starting HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - -"}]}}
|
||||||
|
{"Timestamp":"2022-09-13T02:59:12.3915820+02:00","Level":"Information","MessageTemplate":"{HostingRequestFinishedLog:l}","Properties":{"ElapsedMilliseconds":426.2987,"StatusCode":200,"ContentType":"application/json;charset=utf-8","ContentLength":null,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:5001","PathBase":"","Path":"/swagger/v1/swagger.json","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - - - 200 - application/json;charset=utf-8 426.2987ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"0HMKL4CJ4LVHE:00000007","RequestPath":"/swagger/v1/swagger.json","ConnectionId":"0HMKL4CJ4LVHE","Environment":"Microsoft.AspNetCore.Hosting.HostingEnvironment"},"Renderings":{"HostingRequestFinishedLog":[{"Format":"l","Rendering":"Request finished HTTP/2 GET https://localhost:5001/swagger/v1/swagger.json - - - 200 - application/json;charset=utf-8 426.2987ms"}]}}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Managing.Common\Managing.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Managing.Domain\Managing.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Managing.Domain.Accounts;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface IAccountRepository
|
||||||
|
{
|
||||||
|
Task<Account> GetAccountByNameAsync(string name);
|
||||||
|
Task<Account> GetAccountByKeyAsync(string key);
|
||||||
|
Task InsertAccountAsync(Account account);
|
||||||
|
void DeleteAccountByName(string name);
|
||||||
|
IEnumerable<Account> GetAccounts();
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions;
|
||||||
|
|
||||||
|
public interface IBacktestRepository
|
||||||
|
{
|
||||||
|
void InsertBacktest(Backtest result);
|
||||||
|
IEnumerable<Backtest> GetBacktests();
|
||||||
|
void DeleteBacktestById(string id);
|
||||||
|
void DeleteAllBacktests();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Managing.Common;
|
||||||
|
using Managing.Domain.Candles;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface ICandleRepository
|
||||||
|
{
|
||||||
|
Task<IList<Candle>> GetCandles(
|
||||||
|
Enums.TradingExchanges exchange,
|
||||||
|
Enums.Ticker ticker,
|
||||||
|
Enums.Timeframe timeframe,
|
||||||
|
DateTime start);
|
||||||
|
Task<IList<Enums.Ticker>> GetTickersAsync(
|
||||||
|
Enums.TradingExchanges exchange,
|
||||||
|
Enums.Timeframe timeframe,
|
||||||
|
DateTime start);
|
||||||
|
void InsertCandle(Candle candle);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Managing.Domain.Accounts;
|
||||||
|
using Managing.Domain.Candles;
|
||||||
|
using Managing.Domain.Evm;
|
||||||
|
using Managing.Domain.Trades;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface IEvmManager
|
||||||
|
{
|
||||||
|
(string Key, string Secret) GenerateAddress();
|
||||||
|
string GetAddressFromMnemo(string mnemo);
|
||||||
|
Task<decimal> GetAddressBalance(string address);
|
||||||
|
Task<DateTime> GetBlockDate(int blockNumber);
|
||||||
|
Task<List<Holder>> GetContractHolders(string contractAddress, DateTime since);
|
||||||
|
string SignMessage(string message, string privateKey);
|
||||||
|
string VerifySignature(string signature, string message);
|
||||||
|
Task<List<EvmBalance>> GetBalances(Chain chain, int page, int pageSize, string publicAddress);
|
||||||
|
Task<List<EvmBalance>> GetAllBalancesOnAllChain(string publicAddress);
|
||||||
|
Task<List<Candle>> GetCandles(SubgraphProvider subgraphProvider, Ticker ticker, DateTime startDate, Timeframe interval);
|
||||||
|
decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker);
|
||||||
|
Task<List<Ticker>> GetAvailableTicker();
|
||||||
|
Task<Candle> GetCandle(SubgraphProvider subgraphProvider, Ticker ticker);
|
||||||
|
Task<bool> InitAddress(string chainName, string publicAddress, string privateKey);
|
||||||
|
Task<bool> Send(Chain chain, Ticker ticker, decimal amount, string publicAddress, string privateKey, string receiverAddress);
|
||||||
|
Task<EvmBalance> GetTokenBalance(string chainName, Ticker ticker, string publicAddress);
|
||||||
|
Task<bool> CancelOrders(Account account, Ticker ticker);
|
||||||
|
Task<Trade> IncreasePosition(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = 1);
|
||||||
|
Task<Trade> GetTrade(Account account, string chainName, Ticker ticker);
|
||||||
|
Task<Trade> DecreasePosition(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage);
|
||||||
|
Task<decimal> QuantityInPosition(string chainName, string publicAddress, Ticker ticker);
|
||||||
|
Task<Trade> DecreaseOrder(Account account, TradeType tradeType, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage);
|
||||||
|
Task<decimal> GetFee(string chainName);
|
||||||
|
Task<List<Trade>> GetOrders(Account account, Ticker ticker);
|
||||||
|
Task<Trade> GetTrade(string reference, string arbitrum, Ticker ticker);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Managing.Domain.MoneyManagements;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface ISettingsRepository
|
||||||
|
{
|
||||||
|
Task<MoneyManagement> GetMoneyManagement(string name);
|
||||||
|
Task InsertMoneyManagement(MoneyManagement request);
|
||||||
|
void UpdateMoneyManagement(MoneyManagement moneyManagement);
|
||||||
|
IEnumerable<MoneyManagement> GetMoneyManagements();
|
||||||
|
void DeleteMoneyManagement(string name);
|
||||||
|
void DeleteMoneyManagements();
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Managing.Domain.Statistics;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface IStatisticRepository
|
||||||
|
{
|
||||||
|
Task InsertTopVolumeTicker(TopVolumeTicker topVolumeTicker);
|
||||||
|
IList<TopVolumeTicker> GetTopVolumeTickers(DateTime date);
|
||||||
|
Task SaveSpotligthtOverview(SpotlightOverview overview);
|
||||||
|
IList<SpotlightOverview> GetSpotlightOverviews(DateTime date);
|
||||||
|
void UpdateSpotlightOverview(SpotlightOverview overview);
|
||||||
|
List<Trader> GetBestTraders();
|
||||||
|
void UpdateBestTrader(Trader trader);
|
||||||
|
Task InsertBestTrader(Trader trader);
|
||||||
|
Task RemoveBestTrader(Trader trader);
|
||||||
|
List<Trader> GetBadTraders();
|
||||||
|
void UpdateBadTrader(Trader trader);
|
||||||
|
Task InsertBadTrader(Trader trader);
|
||||||
|
Task RemoveBadTrader(Trader trader);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Managing.Domain.Scenarios;
|
||||||
|
using Managing.Domain.Strategies;
|
||||||
|
using Managing.Domain.Trades;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface ITradingRepository
|
||||||
|
{
|
||||||
|
Scenario GetScenarioByName(string scenario);
|
||||||
|
void InsertSignal(Signal signal);
|
||||||
|
void InsertPosition(Position position);
|
||||||
|
void UpdatePosition(Position position);
|
||||||
|
Strategy GetStrategyByName(string strategy);
|
||||||
|
void InsertScenario(Scenario scenario);
|
||||||
|
void InsertStrategy(Strategy strategy);
|
||||||
|
IEnumerable<Scenario> GetScenarios();
|
||||||
|
IEnumerable<Strategy> GetStrategies();
|
||||||
|
void DeleteScenario(string name);
|
||||||
|
void DeleteStrategy(string name);
|
||||||
|
void DeleteScenarios();
|
||||||
|
void DeleteStrategies();
|
||||||
|
Position GetPositionByIdentifier(string identifier);
|
||||||
|
IEnumerable<Position> GetPositions(PositionInitiator positionInitiator);
|
||||||
|
IEnumerable<Position> GetPositionsByStatus(PositionStatus positionStatus);
|
||||||
|
Fee GetFee(TradingExchanges exchange);
|
||||||
|
void InsertFee(Fee fee);
|
||||||
|
void UpdateFee(Fee fee);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Managing.Domain.Users;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface IUserRepository
|
||||||
|
{
|
||||||
|
Task<User> GetUserByNameAsync(string name);
|
||||||
|
Task InsertUserAsync(User user);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Managing.Common;
|
||||||
|
using Managing.Domain.Workers;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface IWorkerRepository
|
||||||
|
{
|
||||||
|
Task DisableWorker(Enums.WorkerType workerType);
|
||||||
|
Task EnableWorker(Enums.WorkerType workerType);
|
||||||
|
Task<Worker> GetWorkerAsync(Enums.WorkerType workerType);
|
||||||
|
IEnumerable<Worker> GetWorkers();
|
||||||
|
Task InsertWorker(Worker worker);
|
||||||
|
Task UpdateWorker(Enums.WorkerType workerType, int executionCount);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Managing.Domain.Workflows.Synthetics;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Repositories;
|
||||||
|
|
||||||
|
public interface IWorkflowRepository
|
||||||
|
{
|
||||||
|
bool DeleteWorkflow(string name);
|
||||||
|
Task<SyntheticWorkflow> GetWorkflow(string name);
|
||||||
|
IEnumerable<SyntheticWorkflow> GetWorkflows();
|
||||||
|
Task InsertWorkflow(SyntheticWorkflow workflow);
|
||||||
|
Task UpdateWorkflow(SyntheticWorkflow workflow);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Managing.Domain.Accounts;
|
||||||
|
using Managing.Domain.Users;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IAccountService
|
||||||
|
{
|
||||||
|
Task<Account> CreateAccount(User user, Account account);
|
||||||
|
bool DeleteAccount(User user, string name);
|
||||||
|
IEnumerable<Account> GetAccountsByUser(User user, bool hideSecrets = true);
|
||||||
|
IEnumerable<Account> GetAccounts(bool hideSecrets, bool getBalance);
|
||||||
|
Task<Account> GetAccount(string name, bool hideSecrets, bool getBalance);
|
||||||
|
Task<Account> GetAccountByUser(User user, string name, bool hideSecrets, bool getBalance);
|
||||||
|
Task<Account> GetAccountByKey(string key, bool hideSecrets, bool getBalance);
|
||||||
|
IEnumerable<Account> GetAccountsBalancesByUser(User user, bool hideSecrets = true);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Managing.Domain.Accounts;
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
|
using Managing.Domain.Candles;
|
||||||
|
using Managing.Domain.MoneyManagements;
|
||||||
|
using Managing.Domain.Scenarios;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions
|
||||||
|
{
|
||||||
|
public interface IBacktester
|
||||||
|
{
|
||||||
|
Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker, Scenario scenario, Timeframe timeframe, double days, decimal balance, bool isForWatchingOnly = false, bool save = false);
|
||||||
|
Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker, Scenario scenario, Timeframe timeframe, double days, decimal balance, bool isForWatchingOnly = false, bool save = false);
|
||||||
|
IEnumerable<Backtest> GetBacktests();
|
||||||
|
bool DeleteBacktest(string id);
|
||||||
|
bool DeleteBacktests();
|
||||||
|
Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List<Candle> candles, decimal balance);
|
||||||
|
Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List<Candle> candles, decimal balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Managing.Domain.Statistics;
|
||||||
|
using Managing.Domain.Trades;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IDiscordService
|
||||||
|
{
|
||||||
|
Task SendSignal(string message, TradingExchanges exchange, Ticker ticker, TradeDirection direction, Timeframe timeframe);
|
||||||
|
Task SendPosition(Position position);
|
||||||
|
Task SendClosingPosition(Position position);
|
||||||
|
Task SendMessage(string v);
|
||||||
|
Task SendTradeMessage(string message, bool isBadBehavior = false);
|
||||||
|
Task SendIncreasePosition(string address, Trade trade, string copyAccountName, Trade? oldTrade = null);
|
||||||
|
Task SendClosedPosition(string address, Trade oldTrade);
|
||||||
|
Task SendDecreasePosition(string address, Trade newTrade, decimal decreaseAmount);
|
||||||
|
Task SendBestTraders(List<Trader> traders);
|
||||||
|
Task SendBadTraders(List<Trader> traders);
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using Managing.Domain.Trades;
|
||||||
|
using Managing.Domain.Candles;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
using Managing.Domain.Accounts;
|
||||||
|
|
||||||
|
namespace Managing.Application.Abstractions.Services;
|
||||||
|
|
||||||
|
public interface IExchangeService
|
||||||
|
{
|
||||||
|
Task<Trade> OpenTrade(
|
||||||
|
Account account,
|
||||||
|
Ticker ticker,
|
||||||
|
TradeDirection direction,
|
||||||
|
decimal price,
|
||||||
|
decimal quantity,
|
||||||
|
decimal? leverage = null,
|
||||||
|
TradeType tradeType = TradeType.Limit,
|
||||||
|
bool reduceOnly = false,
|
||||||
|
bool isForPaperTrading = false,
|
||||||
|
DateTime? currentDate = null,
|
||||||
|
bool ioc = true);
|
||||||
|
Task<decimal> GetBalance(Account account, bool isForPaperTrading = false);
|
||||||
|
Task<List<Balance>> GetBalances(Account account, bool isForPaperTrading = false);
|
||||||
|
decimal GetPrice(Account account, Ticker ticker, DateTime date);
|
||||||
|
Task<Trade> GetTrade(Account account, string order, Ticker ticker);
|
||||||
|
Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval);
|
||||||
|
Task<Trade> OpenStopLoss(Account account, Ticker ticker, TradeDirection originalDirection, decimal stopLossPrice,
|
||||||
|
decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null);
|
||||||
|
Task<List<Ticker>> GetTickers(Account account, Timeframe timeframe);
|
||||||
|
Task<Trade> OpenTakeProfit(Account account, Ticker ticker, TradeDirection originalDirection, decimal takeProfitPrice,
|
||||||
|
decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null);
|
||||||
|
Task<Trade> ClosePosition(Account account, Position position, decimal lastPrice, bool isForPaperTrading = false);
|
||||||
|
decimal GetVolume(Account account, Ticker ticker);
|
||||||
|
Task<List<Trade>> GetTrades(Account account, Ticker ticker);
|
||||||
|
Task<bool> CancelOrder(Account account, Ticker ticker);
|
||||||
|
decimal GetFee(Account account, bool isForPaperTrading = false);
|
||||||
|
Candle GetCandle(Account account, Ticker ticker, DateTime date);
|
||||||
|
Task<decimal> GetQuantityInPosition(Account account, Ticker ticker);
|
||||||
|
Task<List<Candle>> GetCandlesInflux(TradingExchanges exchange, Ticker ticker, DateTime startDate, Timeframe timeframe);
|
||||||
|
decimal GetBestPrice(Account account, Ticker ticker, decimal lastPrice, decimal quantity, TradeDirection direction);
|
||||||
|
Orderbook GetOrderbook(Account account, Ticker ticker);
|
||||||
|
Trade BuildEmptyTrade(Ticker ticker, decimal price, decimal quantity, TradeDirection direction, decimal? leverage, TradeType tradeType, DateTime dateTime, TradeStatus tradeStatus = TradeStatus.PendingOpen);
|
||||||
|
Task<List<Trade>> GetOpenOrders(Account account, Ticker ticker);
|
||||||
|
Task<Trade> GetTrade(string reference, string orderId, Ticker ticker);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user