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