commit 464a8730e8be98ebe591e005a1ca880ff9f6b556 Author: alirehmani Date: Fri May 3 16:39:25 2024 +0500 docker files fixes from liaqat diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f0f644e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +LICENSE +README.md diff --git a/.github/workflows/caprover.yml b/.github/workflows/caprover.yml new file mode 100644 index 0000000..6cf9e0d --- /dev/null +++ b/.github/workflows/caprover.yml @@ -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 }}' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73e451a --- /dev/null +++ b/.gitignore @@ -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 diff --git a/Documentation.md b/Documentation.md new file mode 100644 index 0000000..c047df3 --- /dev/null +++ b/Documentation.md @@ -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 + +![InfluxDb setup](assets/img/doc-influxdb.png) + +- Go to api keys + +![InfluxDb Api](assets/img/doc-influxdb-apikeys-1.png) +- Create an apikey and update the ```apps\src\Managing.Api.Workers\appsettings.MYNAME.json``` file with the api key + +![InfluxDb Api](assets/img/doc-influxdb-apikeys-2.png) +- Then redeploy by executing : ```.\scripts\docker-deploy-sandbox.cmd``` +- Check influxdb > buckets > prices-bucket > if there is data + +![Docker](assets/img/doc-docker.png) + +- 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``` +![Api Settings](assets/img/doc-settings.png) + +# 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\``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..354145c --- /dev/null +++ b/README.md @@ -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 +![alt text](assets/img/Architecture.png) + +## 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 | + diff --git a/assets/NSwagConfig.nswag b/assets/NSwagConfig.nswag new file mode 100644 index 0000000..df2ce56 --- /dev/null +++ b/assets/NSwagConfig.nswag @@ -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" + } + } +} \ No newline at end of file diff --git a/assets/Todo-v1.txt b/assets/Todo-v1.txt new file mode 100644 index 0000000..8d845da --- /dev/null +++ b/assets/Todo-v1.txt @@ -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 diff --git a/assets/Todo-v2.md b/assets/Todo-v2.md new file mode 100644 index 0000000..8f4c220 --- /dev/null +++ b/assets/Todo-v2.md @@ -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 diff --git a/assets/documentation/Architecture.drawio b/assets/documentation/Architecture.drawio new file mode 100644 index 0000000..3d7e070 --- /dev/null +++ b/assets/documentation/Architecture.drawio @@ -0,0 +1,1586 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/documentation/EndGame.md b/assets/documentation/EndGame.md new file mode 100644 index 0000000..66a7c87 --- /dev/null +++ b/assets/documentation/EndGame.md @@ -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] + +``` \ No newline at end of file diff --git a/assets/documentation/Flows.md b/assets/documentation/Flows.md new file mode 100644 index 0000000..c351076 --- /dev/null +++ b/assets/documentation/Flows.md @@ -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() + } + +``` \ No newline at end of file diff --git a/assets/documentation/PositionWorkflow.md b/assets/documentation/PositionWorkflow.md new file mode 100644 index 0000000..e58bc05 --- /dev/null +++ b/assets/documentation/PositionWorkflow.md @@ -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 +``` \ No newline at end of file diff --git a/assets/img/Architecture.png b/assets/img/Architecture.png new file mode 100644 index 0000000..8490956 Binary files /dev/null and b/assets/img/Architecture.png differ diff --git a/assets/img/doc-docker.png b/assets/img/doc-docker.png new file mode 100644 index 0000000..f83c43a Binary files /dev/null and b/assets/img/doc-docker.png differ diff --git a/assets/img/doc-influxdb-apikeys-1.png b/assets/img/doc-influxdb-apikeys-1.png new file mode 100644 index 0000000..ee662cd Binary files /dev/null and b/assets/img/doc-influxdb-apikeys-1.png differ diff --git a/assets/img/doc-influxdb-apikeys-2.png b/assets/img/doc-influxdb-apikeys-2.png new file mode 100644 index 0000000..29a225f Binary files /dev/null and b/assets/img/doc-influxdb-apikeys-2.png differ diff --git a/assets/img/doc-influxdb.png b/assets/img/doc-influxdb.png new file mode 100644 index 0000000..6caf9ed Binary files /dev/null and b/assets/img/doc-influxdb.png differ diff --git a/assets/img/doc-settings.png b/assets/img/doc-settings.png new file mode 100644 index 0000000..37d967f Binary files /dev/null and b/assets/img/doc-settings.png differ diff --git a/captain-definition b/captain-definition new file mode 100644 index 0000000..4ba5158 --- /dev/null +++ b/captain-definition @@ -0,0 +1,4 @@ +{ + "schemaVersion": 2, + "dockerfilePath": "src/Managing.Api/Dockerfil" +} diff --git a/scripts/build_and_run.sh b/scripts/build_and_run.sh new file mode 100644 index 0000000..be69230 --- /dev/null +++ b/scripts/build_and_run.sh @@ -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 \ No newline at end of file diff --git a/scripts/clean-front-end-code.cmd b/scripts/clean-front-end-code.cmd new file mode 100644 index 0000000..a2c3272 --- /dev/null +++ b/scripts/clean-front-end-code.cmd @@ -0,0 +1,4 @@ +cd .. +cd .\src\Managing.WebApp\ +npm run lint:fix +npm run prettier \ No newline at end of file diff --git a/scripts/docker-deploy-sandbox - Copy.cmd b/scripts/docker-deploy-sandbox - Copy.cmd new file mode 100644 index 0000000..e554517 --- /dev/null +++ b/scripts/docker-deploy-sandbox - Copy.cmd @@ -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 \ No newline at end of file diff --git a/scripts/docker-deploy-sandbox.cmd b/scripts/docker-deploy-sandbox.cmd new file mode 100644 index 0000000..e554517 --- /dev/null +++ b/scripts/docker-deploy-sandbox.cmd @@ -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 \ No newline at end of file diff --git a/scripts/docker-redeploy-oda.cmd b/scripts/docker-redeploy-oda.cmd new file mode 100644 index 0000000..8b6cfa5 --- /dev/null +++ b/scripts/docker-redeploy-oda.cmd @@ -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" \ No newline at end of file diff --git a/src/.dockerignore b/src/.dockerignore new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/.dockerignore @@ -0,0 +1 @@ + diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 0000000..cb4f5d0 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# IDE0008: Use explicit type +dotnet_diagnostic.IDE0008.severity = none diff --git a/src/Managing.Api.Workers.csproj b/src/Managing.Api.Workers.csproj new file mode 100644 index 0000000..c3d9236 --- /dev/null +++ b/src/Managing.Api.Workers.csproj @@ -0,0 +1,45 @@ + + + + net7.0 + enable + AnyCPU;x64 + 3900ce93-de15-49e5-9a61-7dc2209939ca + Linux + ..\.. + ..\..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + diff --git a/src/Managing.Api.Workers/Controllers/WorkerController.cs b/src/Managing.Api.Workers/Controllers/WorkerController.cs new file mode 100644 index 0000000..3a5bc48 --- /dev/null +++ b/src/Managing.Api.Workers/Controllers/WorkerController.cs @@ -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> GetWorkers() + { + return Ok(_workerService.GetWorkers()); + } + + [HttpPatch] + public async Task ToggleWorker(WorkerType workerType) + { + return Ok(await _workerService.ToggleWorker(workerType)); + } +} diff --git a/src/Managing.Api.Workers/Dockerfile b/src/Managing.Api.Workers/Dockerfile new file mode 100644 index 0000000..9e6e534 --- /dev/null +++ b/src/Managing.Api.Workers/Dockerfile @@ -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"] diff --git a/src/Managing.Api.Workers/Filters/EnumSchemaFilter.cs b/src/Managing.Api.Workers/Filters/EnumSchemaFilter.cs new file mode 100644 index 0000000..063f4ae --- /dev/null +++ b/src/Managing.Api.Workers/Filters/EnumSchemaFilter.cs @@ -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))); + } + } + } +} diff --git a/src/Managing.Api.Workers/Managing.Api.Workers.csproj b/src/Managing.Api.Workers/Managing.Api.Workers.csproj new file mode 100644 index 0000000..b3b1706 --- /dev/null +++ b/src/Managing.Api.Workers/Managing.Api.Workers.csproj @@ -0,0 +1,45 @@ + + + + net7.0 + enable + AnyCPU;x64 + 3900ce93-de15-49e5-9a61-7dc2209939ca + Linux + ..\.. + ..\..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + diff --git a/src/Managing.Api.Workers/Program.cs b/src/Managing.Api.Workers/Program.cs new file mode 100644 index 0000000..cd36f40 --- /dev/null +++ b/src/Managing.Api.Workers/Program.cs @@ -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(builder.Configuration.GetSection(Constants.Databases.MongoDb)); +builder.Services.Configure(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(), 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(); + 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(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); + +// 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"); +}); + +app.Run(); diff --git a/src/Managing.Api.Workers/Workers/BaseWorker.cs b/src/Managing.Api.Workers/Workers/BaseWorker.cs new file mode 100644 index 0000000..cc11430 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/BaseWorker.cs @@ -0,0 +1,75 @@ + +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers; + +public abstract class BaseWorker : BackgroundService where T : class +{ + private readonly WorkerType _workerType; + protected readonly ILogger _logger; + protected readonly TimeSpan _delay; + private readonly IWorkerService _workerService; + private int _executionCount; + + protected BaseWorker( + WorkerType workerType, + ILogger 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); +} diff --git a/src/Managing.Api.Workers/Workers/FeeWorker.cs b/src/Managing.Api.Workers/Workers/FeeWorker.cs new file mode 100644 index 0000000..a0aa579 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/FeeWorker.cs @@ -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 +{ + private readonly ITradingService _tradingService; + private static readonly WorkerType _workerType = WorkerType.Fee; + + public FeeWorker( + ILogger 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); + } +} diff --git a/src/Managing.Api.Workers/Workers/LeaderboardWorker.cs b/src/Managing.Api.Workers/Workers/LeaderboardWorker.cs new file mode 100644 index 0000000..96feb0e --- /dev/null +++ b/src/Managing.Api.Workers/Workers/LeaderboardWorker.cs @@ -0,0 +1,28 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class LeaderboardWorker : BaseWorker +{ + private readonly IStatisticService _statisticService; + private static readonly WorkerType _workerType = WorkerType.LeaderboardWorker; + + public LeaderboardWorker( + ILogger logger, + IStatisticService statisticService, + IWorkerService workerService) : base( + _workerType, + logger, + TimeSpan.FromHours(24), + workerService + ) + { + _statisticService = statisticService; + } + + protected override async Task Run(CancellationToken cancellationToken) + { + await _statisticService.UpdateLeaderboard(); + } +} diff --git a/src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs b/src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs new file mode 100644 index 0000000..13aa38a --- /dev/null +++ b/src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs @@ -0,0 +1,28 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class NoobiesboardWorker : BaseWorker +{ + private readonly IStatisticService _statisticService; + private static readonly WorkerType _workerType = WorkerType.Noobiesboard; + + public NoobiesboardWorker( + ILogger logger, + IStatisticService statisticService, + IWorkerService workerService) : base( + _workerType, + logger, + TimeSpan.FromHours(24), + workerService + ) + { + _statisticService = statisticService; + } + + protected override async Task Run(CancellationToken cancellationToken) + { + await _statisticService.UpdateNoobiesboard(); + } +} diff --git a/src/Managing.Api.Workers/Workers/PositionFetcher.cs b/src/Managing.Api.Workers/Workers/PositionFetcher.cs new file mode 100644 index 0000000..4e66188 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PositionFetcher.cs @@ -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 +{ + private static readonly WorkerType _workerType = WorkerType.PositionFetcher; + private readonly ITradingService _tradingService; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; + + public PositionFetcher( + ILogger logger, + IWorkerService workerService, + ITradingService tradingService, + + IHubContext 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); + } +} diff --git a/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs b/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs new file mode 100644 index 0000000..61811e5 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs @@ -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 +{ + private static readonly WorkerType _workerType = WorkerType.PositionManager; + private readonly ITradingService _tradingService; + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + private readonly ILogger _logger; + + public PositionManagerWorker( + ILogger 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 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); + } + } + +} diff --git a/src/Managing.Api.Workers/Workers/PricesBaseWorker.cs b/src/Managing.Api.Workers/Workers/PricesBaseWorker.cs new file mode 100644 index 0000000..d3816e2 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PricesBaseWorker.cs @@ -0,0 +1,40 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public abstract class PricesBaseWorker : BaseWorker where T : class +{ + private readonly IPricesService _pricesService; + private readonly IStatisticService _statisticService; + private readonly Timeframe _timeframe; + + public PricesBaseWorker( + ILogger 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); + } + } +} diff --git a/src/Managing.Api.Workers/Workers/PricesFifteenMinutesWorker.cs b/src/Managing.Api.Workers/Workers/PricesFifteenMinutesWorker.cs new file mode 100644 index 0000000..88df774 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PricesFifteenMinutesWorker.cs @@ -0,0 +1,23 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class PricesFifteenMinutesWorker : PricesBaseWorker +{ + public PricesFifteenMinutesWorker( + ILogger logger, + IPricesService pricesService, + IStatisticService statisticService, + IWorkerService workerService) : base( + logger, + pricesService, + workerService, + statisticService, + TimeSpan.FromMinutes(1), + WorkerType.PriceFifteenMinutes, + Timeframe.FifteenMinutes + ) + { + } +} diff --git a/src/Managing.Api.Workers/Workers/PricesFiveMinutesWorker.cs b/src/Managing.Api.Workers/Workers/PricesFiveMinutesWorker.cs new file mode 100644 index 0000000..8dff08f --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PricesFiveMinutesWorker.cs @@ -0,0 +1,23 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class PricesFiveMinutesWorker : PricesBaseWorker +{ + public PricesFiveMinutesWorker( + ILogger logger, + IPricesService pricesService, + IStatisticService statisticService, + IWorkerService workerService) : base( + logger, + pricesService, + workerService, + statisticService, + TimeSpan.FromMinutes(2.5), + WorkerType.PriceFiveMinutes, + Timeframe.FiveMinutes + ) + { + } +} diff --git a/src/Managing.Api.Workers/Workers/PricesFourHoursWorker.cs b/src/Managing.Api.Workers/Workers/PricesFourHoursWorker.cs new file mode 100644 index 0000000..2b49931 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PricesFourHoursWorker.cs @@ -0,0 +1,23 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class PricesFourHoursWorker : PricesBaseWorker +{ + public PricesFourHoursWorker( + ILogger logger, + IPricesService pricesService, + IStatisticService statisticService, + IWorkerService workerService) : base( + logger, + pricesService, + workerService, + statisticService, + TimeSpan.FromHours(2), + WorkerType.PriceFourHour, + Timeframe.FourHour + ) + { + } +} diff --git a/src/Managing.Api.Workers/Workers/PricesOneDayWorker.cs b/src/Managing.Api.Workers/Workers/PricesOneDayWorker.cs new file mode 100644 index 0000000..7de871a --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PricesOneDayWorker.cs @@ -0,0 +1,23 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class PricesOneDayWorker : PricesBaseWorker +{ + public PricesOneDayWorker( + ILogger logger, + IPricesService pricesService, + IStatisticService statisticService, + IWorkerService workerService) : base( + logger, + pricesService, + workerService, + statisticService, + TimeSpan.FromHours(12), + WorkerType.PriceOneDay, + Timeframe.OneDay + ) + { + } +} diff --git a/src/Managing.Api.Workers/Workers/PricesOneHourWorker.cs b/src/Managing.Api.Workers/Workers/PricesOneHourWorker.cs new file mode 100644 index 0000000..ce0b537 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/PricesOneHourWorker.cs @@ -0,0 +1,22 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class PricesOneHourWorker : PricesBaseWorker +{ + public PricesOneHourWorker( + ILogger logger, + IPricesService pricesService, + IStatisticService statisticService, + IWorkerService workerService) : base( + logger, + pricesService, + workerService, + statisticService, + TimeSpan.FromMinutes(30), + WorkerType.PriceOneHour, + Timeframe.OneHour) + { + } +} diff --git a/src/Managing.Api.Workers/Workers/SpotlightWorker.cs b/src/Managing.Api.Workers/Workers/SpotlightWorker.cs new file mode 100644 index 0000000..91e550e --- /dev/null +++ b/src/Managing.Api.Workers/Workers/SpotlightWorker.cs @@ -0,0 +1,34 @@ +using Managing.Application.Workers.Abstractions; +using Managing.Common; + +namespace Managing.Api.Workers.Workers; + +public class SpotlightWorker : BaseWorker +{ + private readonly IStatisticService _statisticService; + + public SpotlightWorker( + ILogger 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; + } + } +} diff --git a/src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs b/src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs new file mode 100644 index 0000000..e103728 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs @@ -0,0 +1,28 @@ +using Managing.Application.Workers.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Api.Workers.Workers; + +public class TopVolumeTickerWorker : BaseWorker +{ + private readonly IStatisticService _statisticService; + private static readonly WorkerType _workerType = WorkerType.TopVolumeTicker; + + public TopVolumeTickerWorker( + ILogger 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); + } +} diff --git a/src/Managing.Api.Workers/Workers/TraderWatcher.cs b/src/Managing.Api.Workers/Workers/TraderWatcher.cs new file mode 100644 index 0000000..90895a5 --- /dev/null +++ b/src/Managing.Api.Workers/Workers/TraderWatcher.cs @@ -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 +{ + private readonly ITradingService _tradingService; + private static readonly WorkerType _workerType = WorkerType.TraderWatcher; + + public TraderWatcher( + ILogger logger, + ITradingService tradingService, + IWorkerService workerService) : base( + _workerType, + logger, + TimeSpan.FromSeconds(120), + workerService + ) + { + _tradingService = tradingService; + } + + protected override async Task Run(CancellationToken cancellationToken) + { + await _tradingService.WatchTrader(); + } +} diff --git a/src/Managing.Api.Workers/appsettings.Development.json b/src/Managing.Api.Workers/appsettings.Development.json new file mode 100644 index 0000000..b273bce --- /dev/null +++ b/src/Managing.Api.Workers/appsettings.Development.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api.Workers/appsettings.Khalid.json b/src/Managing.Api.Workers/appsettings.Khalid.json new file mode 100644 index 0000000..d9754cb --- /dev/null +++ b/src/Managing.Api.Workers/appsettings.Khalid.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api.Workers/appsettings.Lowpro.json b/src/Managing.Api.Workers/appsettings.Lowpro.json new file mode 100644 index 0000000..72246f9 --- /dev/null +++ b/src/Managing.Api.Workers/appsettings.Lowpro.json @@ -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": "*" +} diff --git a/src/Managing.Api.Workers/appsettings.Oda-docker.json b/src/Managing.Api.Workers/appsettings.Oda-docker.json new file mode 100644 index 0000000..e81d760 --- /dev/null +++ b/src/Managing.Api.Workers/appsettings.Oda-docker.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api.Workers/appsettings.Oda.json b/src/Managing.Api.Workers/appsettings.Oda.json new file mode 100644 index 0000000..d9754cb --- /dev/null +++ b/src/Managing.Api.Workers/appsettings.Oda.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api/Authorization/JwtMiddleware.cs b/src/Managing.Api/Authorization/JwtMiddleware.cs new file mode 100644 index 0000000..cc01bfd --- /dev/null +++ b/src/Managing.Api/Authorization/JwtMiddleware.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Managing.Api/Authorization/JwtUtils.cs b/src/Managing.Api/Authorization/JwtUtils.cs new file mode 100644 index 0000000..ad60090 --- /dev/null +++ b/src/Managing.Api/Authorization/JwtUtils.cs @@ -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("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; + } + } +} diff --git a/src/Managing.Api/Controllers/AccountController.cs b/src/Managing.Api/Controllers/AccountController.cs new file mode 100644 index 0000000..845355e --- /dev/null +++ b/src/Managing.Api/Controllers/AccountController.cs @@ -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> PostAccount(Account Account) + { + var user = await GetUser(); + return Ok(await _AccountService.CreateAccount(user, Account)); + } + + [HttpGet] + [Route("accounts")] + public async Task>> GetAccounts() + { + var user = await GetUser(); + return Ok(_AccountService.GetAccountsByUser(user, true)); + } + + [HttpGet] + [Route("balances")] + public async Task>> GetAccountsBalances() + { + var user = await GetUser(); + return Ok(_AccountService.GetAccountsBalancesByUser(user)); + } + + [HttpGet] + public async Task> 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)); + } + } +} diff --git a/src/Managing.Api/Controllers/BacktestController.cs b/src/Managing.Api/Controllers/BacktestController.cs new file mode 100644 index 0000000..22558e7 --- /dev/null +++ b/src/Managing.Api/Controllers/BacktestController.cs @@ -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 _hubContext; + private readonly IBacktester _backtester; + private readonly IScenarioService _scenarioService; + private readonly IAccountService _accountService; + private readonly IMoneyManagementService _moneyManagementService; + + public BacktestController( + IHubContext hubContext, + IBacktester backtester, + IScenarioService scenarioService, + IAccountService accountService, + IMoneyManagementService moneyManagementService) + { + _hubContext = hubContext; + _backtester = backtester; + _scenarioService = scenarioService; + _accountService = accountService; + _moneyManagementService = moneyManagementService; + } + + [HttpGet] + public ActionResult> 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> 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); + } + } +} diff --git a/src/Managing.Api/Controllers/BaseController.cs b/src/Managing.Api/Controllers/BaseController.cs new file mode 100644 index 0000000..70c6fdc --- /dev/null +++ b/src/Managing.Api/Controllers/BaseController.cs @@ -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 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"); + } +} diff --git a/src/Managing.Api/Controllers/BotController.cs b/src/Managing.Api/Controllers/BotController.cs new file mode 100644 index 0000000..7ef588e --- /dev/null +++ b/src/Managing.Api/Controllers/BotController.cs @@ -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 _logger; + private readonly IHubContext _hubContext; + private readonly IBacktester _backtester; + + public BotController(ILogger logger, IMediator mediator, IHubContext hubContext, IBacktester backtester) + { + _logger = logger; + _mediator = mediator; + _hubContext = hubContext; + _backtester = backtester; + } + + [HttpPost] + [Route("Start")] + public async Task> 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> 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> 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 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> 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 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> 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> GetActiveBots() + { + return await GetBotList(); + } + + private async Task> GetBotList() + { + var result = await _mediator.Send(new GetActiveBotsCommand()); + var list = new List(); + + 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); + } +} diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs new file mode 100644 index 0000000..01f0398 --- /dev/null +++ b/src/Managing.Api/Controllers/DataController.cs @@ -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 _hubContext; + + public DataController( + IExchangeService exchangeService, + IAccountService accountService, + ICacheService cacheService, + IStatisticService statisticService, + IHubContext hubContext) + { + _exchangeService = exchangeService; + _accountService = accountService; + _cacheService = cacheService; + _statisticService = statisticService; + _hubContext = hubContext; + } + + [HttpPost("GetTickers")] + public async Task> 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 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>> GetCandles(TradingExchanges exchange, Ticker ticker, DateTime startDate, Timeframe timeframe) + { + return Ok(await _exchangeService.GetCandlesInflux(exchange, ticker, startDate, timeframe)); + } + +} diff --git a/src/Managing.Api/Controllers/MoneyManagementController.cs b/src/Managing.Api/Controllers/MoneyManagementController.cs new file mode 100644 index 0000000..6ce38a9 --- /dev/null +++ b/src/Managing.Api/Controllers/MoneyManagementController.cs @@ -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> PostMoneyManagement(MoneyManagement moneyManagement) + { + return Ok(await _moneyManagementService.CreateOrUpdateMoneyManagement(moneyManagement)); + } + + [HttpGet] + [Route("moneymanagements")] + public ActionResult> GetMoneyManagements() + { + return Ok(_moneyManagementService.GetMoneyMangements()); + } + + [HttpGet] + public ActionResult GetMoneyManagement(string name) + { + return Ok(_moneyManagementService.GetMoneyMangement(name)); + } + + [HttpDelete] + public ActionResult DeleteMoneyManagement(string name) + { + return Ok(_moneyManagementService.DeleteMoneyManagement(name)); + } +} diff --git a/src/Managing.Api/Controllers/ScenarioController.cs b/src/Managing.Api/Controllers/ScenarioController.cs new file mode 100644 index 0000000..2a55c9a --- /dev/null +++ b/src/Managing.Api/Controllers/ScenarioController.cs @@ -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> GetScenarios() + { + return Ok(_scenarioService.GetScenarios()); + } + + [HttpPost] + public ActionResult CreateScenario(string name, List strategies) + { + return Ok(_scenarioService.CreateScenario(name, strategies)); + } + + + [HttpDelete] + public ActionResult DeleteScenario(string name) + { + return Ok(_scenarioService.DeleteScenario(name)); + } + + [HttpGet] + [Route("strategy")] + public ActionResult> GetStrategies() + { + return Ok(_scenarioService.GetStrategies()); + } + + [HttpPost] + [Route("strategy")] + public ActionResult 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)); + } +} diff --git a/src/Managing.Api/Controllers/SettingsController.cs b/src/Managing.Api/Controllers/SettingsController.cs new file mode 100644 index 0000000..f987063 --- /dev/null +++ b/src/Managing.Api/Controllers/SettingsController.cs @@ -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> ResetSettings() + { + return Ok(await _settingsService.ResetSettings()); + } +} diff --git a/src/Managing.Api/Controllers/TradingController.cs b/src/Managing.Api/Controllers/TradingController.cs new file mode 100644 index 0000000..bec3be2 --- /dev/null +++ b/src/Managing.Api/Controllers/TradingController.cs @@ -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 _openTradeCommandHandler; + private readonly ICommandHandler _closeTradeCommandHandler; + private readonly ITradingService _tradingService; + private readonly IMoneyManagementService _moneyManagementService; + private readonly IMediator _mediator; + + private readonly ILogger _logger; + + public TradingController( + ILogger logger, + ICommandHandler openTradeCommandHandler, + ICommandHandler closeTradeCommandHandler, + ITradingService tradingService, + IMediator mediator) + { + _logger = logger; + _openTradeCommandHandler = openTradeCommandHandler; + _closeTradeCommandHandler = closeTradeCommandHandler; + _tradingService = tradingService; + _mediator = mediator; + } + + [HttpGet("GetPositions")] + public async Task>> GetPositions(PositionInitiator positionInitiator) + { + var result = await _mediator.Send(new GetPositionsCommand(positionInitiator)); + return Ok(result); + } + + [HttpGet("GetTrade")] + public async Task> 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> GetTrades(string accountName, Ticker ticker, string exchangeOrderId) + { + var result = await _mediator.Send(new GetTradesCommand(ticker, accountName)); + return Ok(result); + } + + [HttpGet("ClosePosition")] + public async Task> ClosePosition(string identifier) + { + var position = _tradingService.GetPositionByIdentifier(identifier); + var result = await _closeTradeCommandHandler.Handle(new ClosePositionCommand(position)); + return Ok(result); + } + + [HttpGet("OpenPosition")] + public async Task> 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); + } +} diff --git a/src/Managing.Api/Controllers/UserController.cs b/src/Managing.Api/Controllers/UserController.cs new file mode 100644 index 0000000..0ec0c71 --- /dev/null +++ b/src/Managing.Api/Controllers/UserController.cs @@ -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> 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(); + } +} diff --git a/src/Managing.Api/Controllers/WorkflowController.cs b/src/Managing.Api/Controllers/WorkflowController.cs new file mode 100644 index 0000000..2e33a60 --- /dev/null +++ b/src/Managing.Api/Controllers/WorkflowController.cs @@ -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> PostWorkflow([ModelBinder]SyntheticWorkflow workflowRequest) + { + return Ok(await _workflowService.InsertOrUpdateWorkflow(workflowRequest)); + } + + [HttpGet] + public ActionResult> GetWorkflows() + { + return Ok(_workflowService.GetWorkflows()); + } + + [HttpGet] + [Route("flows")] + public async Task>> GetAvailableFlows() + { + return Ok(await _workflowService.GetAvailableFlows()); + } + + [HttpDelete] + public ActionResult DeleteWorkflow(string name) + { + return Ok(_workflowService.DeleteWorkflow(name)); + } + } +} diff --git a/src/Managing.Api/Dockerfile b/src/Managing.Api/Dockerfile new file mode 100644 index 0000000..484044a --- /dev/null +++ b/src/Managing.Api/Dockerfile @@ -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"] diff --git a/src/Managing.Api/Exceptions/GlobalErrorHandlingMiddleware.cs b/src/Managing.Api/Exceptions/GlobalErrorHandlingMiddleware.cs new file mode 100644 index 0000000..84308bb --- /dev/null +++ b/src/Managing.Api/Exceptions/GlobalErrorHandlingMiddleware.cs @@ -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); + } +} diff --git a/src/Managing.Api/Filters/EnumSchemaFilter.cs b/src/Managing.Api/Filters/EnumSchemaFilter.cs new file mode 100644 index 0000000..7cc60ec --- /dev/null +++ b/src/Managing.Api/Filters/EnumSchemaFilter.cs @@ -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))); + } + } + } +} diff --git a/src/Managing.Api/Managing.Api.csproj b/src/Managing.Api/Managing.Api.csproj new file mode 100644 index 0000000..4b1e176 --- /dev/null +++ b/src/Managing.Api/Managing.Api.csproj @@ -0,0 +1,43 @@ + + + + net7.0 + enable + AnyCPU;x64 + ..\..\docker-compose.dcproj + 7476db9f-ade4-414a-a420-e3ab55cb5f8d + Linux + ..\.. + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + diff --git a/src/Managing.Api/Models/Requests/CreateScenarioRequest.cs b/src/Managing.Api/Models/Requests/CreateScenarioRequest.cs new file mode 100644 index 0000000..95ed55d --- /dev/null +++ b/src/Managing.Api/Models/Requests/CreateScenarioRequest.cs @@ -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 Strategies { get; internal set; } + } +} diff --git a/src/Managing.Api/Models/Requests/CreateStrategyRequest.cs b/src/Managing.Api/Models/Requests/CreateStrategyRequest.cs new file mode 100644 index 0000000..88fc80f --- /dev/null +++ b/src/Managing.Api/Models/Requests/CreateStrategyRequest.cs @@ -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; } +} diff --git a/src/Managing.Api/Models/Requests/LoginRequest.cs b/src/Managing.Api/Models/Requests/LoginRequest.cs new file mode 100644 index 0000000..bfeea19 --- /dev/null +++ b/src/Managing.Api/Models/Requests/LoginRequest.cs @@ -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; } +} diff --git a/src/Managing.Api/Models/Requests/RunBacktestRequest.cs b/src/Managing.Api/Models/Requests/RunBacktestRequest.cs new file mode 100644 index 0000000..f9b6cb3 --- /dev/null +++ b/src/Managing.Api/Models/Requests/RunBacktestRequest.cs @@ -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; } + } +} diff --git a/src/Managing.Api/Models/Requests/StartBotRequest.cs b/src/Managing.Api/Models/Requests/StartBotRequest.cs new file mode 100644 index 0000000..a477733 --- /dev/null +++ b/src/Managing.Api/Models/Requests/StartBotRequest.cs @@ -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; } + } +} diff --git a/src/Managing.Api/Models/Responses/TradingBot.cs b/src/Managing.Api/Models/Responses/TradingBot.cs new file mode 100644 index 0000000..e49bff9 --- /dev/null +++ b/src/Managing.Api/Models/Responses/TradingBot.cs @@ -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 Signals { get; internal set; } + [Required] + public List Positions { get; internal set; } + [Required] + public List 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; } + } +} diff --git a/src/Managing.Api/Program.cs b/src/Managing.Api/Program.cs new file mode 100644 index 0000000..ef4d5e7 --- /dev/null +++ b/src/Managing.Api/Program.cs @@ -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(); +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(builder.Configuration.GetSection(Constants.Databases.MongoDb)); +builder.Services.Configure(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(); + +builder.Services.RegisterApiDependencies(builder.Configuration); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddOpenApiDocument(document => +{ + document.AddSecurity("JWT", Enumerable.Empty(), 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(); + 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(); + +app.UseHttpsRedirection(); + +app.UseRouting(); + +app.UseAuthentication(); + +app.UseAuthorization(); + +app.UseEndpoints(endpoints => +{ + endpoints.MapControllers(); + endpoints.MapHub("/bothub"); + endpoints.MapHub("/backtesthub"); + endpoints.MapHub("/candlehub"); +}); + +app.Run(); diff --git a/src/Managing.Api/appsettings.Development.json b/src/Managing.Api/appsettings.Development.json new file mode 100644 index 0000000..c566931 --- /dev/null +++ b/src/Managing.Api/appsettings.Development.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "System": "Error", + "Microsoft": "Warning" + } + }, + "AllowedHosts": "*", + "ElasticConfiguration": { + "Uri": "http://elasticsearch:9200/" + } +} \ No newline at end of file diff --git a/src/Managing.Api/appsettings.Khalid.json b/src/Managing.Api/appsettings.Khalid.json new file mode 100644 index 0000000..46bd192 --- /dev/null +++ b/src/Managing.Api/appsettings.Khalid.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api/appsettings.Lowpro.json b/src/Managing.Api/appsettings.Lowpro.json new file mode 100644 index 0000000..a59bfc6 --- /dev/null +++ b/src/Managing.Api/appsettings.Lowpro.json @@ -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": "*" +} diff --git a/src/Managing.Api/appsettings.Oda-Sandbox.json b/src/Managing.Api/appsettings.Oda-Sandbox.json new file mode 100644 index 0000000..c8c284b --- /dev/null +++ b/src/Managing.Api/appsettings.Oda-Sandbox.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api/appsettings.Oda-docker.json b/src/Managing.Api/appsettings.Oda-docker.json new file mode 100644 index 0000000..e59c1c4 --- /dev/null +++ b/src/Managing.Api/appsettings.Oda-docker.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api/appsettings.Oda.json b/src/Managing.Api/appsettings.Oda.json new file mode 100644 index 0000000..e9b2a8d --- /dev/null +++ b/src/Managing.Api/appsettings.Oda.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api/appsettings.json b/src/Managing.Api/appsettings.json new file mode 100644 index 0000000..9a4f9a0 --- /dev/null +++ b/src/Managing.Api/appsettings.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/Managing.Api/captain-definition b/src/Managing.Api/captain-definition new file mode 100644 index 0000000..61c2e42 --- /dev/null +++ b/src/Managing.Api/captain-definition @@ -0,0 +1,4 @@ +{ + "schemaVersion": 2, + "dockerfilePath": "Dockerfile" +} diff --git a/src/Managing.Api/failures.txt b/src/Managing.Api/failures.txt new file mode 100644 index 0000000..d4d21d2 --- /dev/null +++ b/src/Managing.Api/failures.txt @@ -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"}]}} diff --git a/src/Managing.Application.Abstractions/Managing.Application.Abstractions.csproj b/src/Managing.Application.Abstractions/Managing.Application.Abstractions.csproj new file mode 100644 index 0000000..405284d --- /dev/null +++ b/src/Managing.Application.Abstractions/Managing.Application.Abstractions.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/src/Managing.Application.Abstractions/Repositories/IAccountRepository.cs b/src/Managing.Application.Abstractions/Repositories/IAccountRepository.cs new file mode 100644 index 0000000..7fa7423 --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/IAccountRepository.cs @@ -0,0 +1,12 @@ +using Managing.Domain.Accounts; + +namespace Managing.Application.Abstractions.Repositories; + +public interface IAccountRepository +{ + Task GetAccountByNameAsync(string name); + Task GetAccountByKeyAsync(string key); + Task InsertAccountAsync(Account account); + void DeleteAccountByName(string name); + IEnumerable GetAccounts(); +} diff --git a/src/Managing.Application.Abstractions/Repositories/IBacktestRepository.cs b/src/Managing.Application.Abstractions/Repositories/IBacktestRepository.cs new file mode 100644 index 0000000..314218c --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/IBacktestRepository.cs @@ -0,0 +1,12 @@ + +using Managing.Domain.Backtests; + +namespace Managing.Application.Abstractions; + +public interface IBacktestRepository +{ + void InsertBacktest(Backtest result); + IEnumerable GetBacktests(); + void DeleteBacktestById(string id); + void DeleteAllBacktests(); +} diff --git a/src/Managing.Application.Abstractions/Repositories/ICandleRepository.cs b/src/Managing.Application.Abstractions/Repositories/ICandleRepository.cs new file mode 100644 index 0000000..5aa4c93 --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/ICandleRepository.cs @@ -0,0 +1,18 @@ +using Managing.Common; +using Managing.Domain.Candles; + +namespace Managing.Application.Abstractions.Repositories; + +public interface ICandleRepository +{ + Task> GetCandles( + Enums.TradingExchanges exchange, + Enums.Ticker ticker, + Enums.Timeframe timeframe, + DateTime start); + Task> GetTickersAsync( + Enums.TradingExchanges exchange, + Enums.Timeframe timeframe, + DateTime start); + void InsertCandle(Candle candle); +} diff --git a/src/Managing.Application.Abstractions/Repositories/IEvmManager.cs b/src/Managing.Application.Abstractions/Repositories/IEvmManager.cs new file mode 100644 index 0000000..fb50b97 --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/IEvmManager.cs @@ -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 GetAddressBalance(string address); + Task GetBlockDate(int blockNumber); + Task> GetContractHolders(string contractAddress, DateTime since); + string SignMessage(string message, string privateKey); + string VerifySignature(string signature, string message); + Task> GetBalances(Chain chain, int page, int pageSize, string publicAddress); + Task> GetAllBalancesOnAllChain(string publicAddress); + Task> GetCandles(SubgraphProvider subgraphProvider, Ticker ticker, DateTime startDate, Timeframe interval); + decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker); + Task> GetAvailableTicker(); + Task GetCandle(SubgraphProvider subgraphProvider, Ticker ticker); + Task InitAddress(string chainName, string publicAddress, string privateKey); + Task Send(Chain chain, Ticker ticker, decimal amount, string publicAddress, string privateKey, string receiverAddress); + Task GetTokenBalance(string chainName, Ticker ticker, string publicAddress); + Task CancelOrders(Account account, Ticker ticker); + Task IncreasePosition(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = 1); + Task GetTrade(Account account, string chainName, Ticker ticker); + Task DecreasePosition(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage); + Task QuantityInPosition(string chainName, string publicAddress, Ticker ticker); + Task DecreaseOrder(Account account, TradeType tradeType, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage); + Task GetFee(string chainName); + Task> GetOrders(Account account, Ticker ticker); + Task GetTrade(string reference, string arbitrum, Ticker ticker); +} diff --git a/src/Managing.Application.Abstractions/Repositories/ISettingsRepository.cs b/src/Managing.Application.Abstractions/Repositories/ISettingsRepository.cs new file mode 100644 index 0000000..6aae113 --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/ISettingsRepository.cs @@ -0,0 +1,13 @@ +using Managing.Domain.MoneyManagements; + +namespace Managing.Application.Abstractions.Repositories; + +public interface ISettingsRepository +{ + Task GetMoneyManagement(string name); + Task InsertMoneyManagement(MoneyManagement request); + void UpdateMoneyManagement(MoneyManagement moneyManagement); + IEnumerable GetMoneyManagements(); + void DeleteMoneyManagement(string name); + void DeleteMoneyManagements(); +} diff --git a/src/Managing.Application.Abstractions/Repositories/IStatisticRepository.cs b/src/Managing.Application.Abstractions/Repositories/IStatisticRepository.cs new file mode 100644 index 0000000..3b583fb --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/IStatisticRepository.cs @@ -0,0 +1,20 @@ +using Managing.Domain.Statistics; + +namespace Managing.Application.Abstractions.Repositories; + +public interface IStatisticRepository +{ + Task InsertTopVolumeTicker(TopVolumeTicker topVolumeTicker); + IList GetTopVolumeTickers(DateTime date); + Task SaveSpotligthtOverview(SpotlightOverview overview); + IList GetSpotlightOverviews(DateTime date); + void UpdateSpotlightOverview(SpotlightOverview overview); + List GetBestTraders(); + void UpdateBestTrader(Trader trader); + Task InsertBestTrader(Trader trader); + Task RemoveBestTrader(Trader trader); + List GetBadTraders(); + void UpdateBadTrader(Trader trader); + Task InsertBadTrader(Trader trader); + Task RemoveBadTrader(Trader trader); +} diff --git a/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs new file mode 100644 index 0000000..5f6b0e1 --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs @@ -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 GetScenarios(); + IEnumerable GetStrategies(); + void DeleteScenario(string name); + void DeleteStrategy(string name); + void DeleteScenarios(); + void DeleteStrategies(); + Position GetPositionByIdentifier(string identifier); + IEnumerable GetPositions(PositionInitiator positionInitiator); + IEnumerable GetPositionsByStatus(PositionStatus positionStatus); + Fee GetFee(TradingExchanges exchange); + void InsertFee(Fee fee); + void UpdateFee(Fee fee); +} diff --git a/src/Managing.Application.Abstractions/Repositories/IUserRepository.cs b/src/Managing.Application.Abstractions/Repositories/IUserRepository.cs new file mode 100644 index 0000000..da98f3c --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/IUserRepository.cs @@ -0,0 +1,9 @@ +using Managing.Domain.Users; + +namespace Managing.Application.Abstractions.Repositories; + +public interface IUserRepository +{ + Task GetUserByNameAsync(string name); + Task InsertUserAsync(User user); +} diff --git a/src/Managing.Application.Abstractions/Repositories/IWorkerRepository.cs b/src/Managing.Application.Abstractions/Repositories/IWorkerRepository.cs new file mode 100644 index 0000000..6f11a7b --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/IWorkerRepository.cs @@ -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 GetWorkerAsync(Enums.WorkerType workerType); + IEnumerable GetWorkers(); + Task InsertWorker(Worker worker); + Task UpdateWorker(Enums.WorkerType workerType, int executionCount); +} diff --git a/src/Managing.Application.Abstractions/Repositories/IWorkflowRepository.cs b/src/Managing.Application.Abstractions/Repositories/IWorkflowRepository.cs new file mode 100644 index 0000000..85e0ba4 --- /dev/null +++ b/src/Managing.Application.Abstractions/Repositories/IWorkflowRepository.cs @@ -0,0 +1,12 @@ +using Managing.Domain.Workflows.Synthetics; + +namespace Managing.Application.Abstractions.Repositories; + +public interface IWorkflowRepository +{ + bool DeleteWorkflow(string name); + Task GetWorkflow(string name); + IEnumerable GetWorkflows(); + Task InsertWorkflow(SyntheticWorkflow workflow); + Task UpdateWorkflow(SyntheticWorkflow workflow); +} diff --git a/src/Managing.Application.Abstractions/Services/IAccountService.cs b/src/Managing.Application.Abstractions/Services/IAccountService.cs new file mode 100644 index 0000000..5c96632 --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IAccountService.cs @@ -0,0 +1,16 @@ +using Managing.Domain.Accounts; +using Managing.Domain.Users; + +namespace Managing.Application.Abstractions.Services; + +public interface IAccountService +{ + Task CreateAccount(User user, Account account); + bool DeleteAccount(User user, string name); + IEnumerable GetAccountsByUser(User user, bool hideSecrets = true); + IEnumerable GetAccounts(bool hideSecrets, bool getBalance); + Task GetAccount(string name, bool hideSecrets, bool getBalance); + Task GetAccountByUser(User user, string name, bool hideSecrets, bool getBalance); + Task GetAccountByKey(string key, bool hideSecrets, bool getBalance); + IEnumerable GetAccountsBalancesByUser(User user, bool hideSecrets = true); +} \ No newline at end of file diff --git a/src/Managing.Application.Abstractions/Services/IBacktester.cs b/src/Managing.Application.Abstractions/Services/IBacktester.cs new file mode 100644 index 0000000..799ac4e --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IBacktester.cs @@ -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 GetBacktests(); + bool DeleteBacktest(string id); + bool DeleteBacktests(); + Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List candles, decimal balance); + Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List candles, decimal balance); + } +} \ No newline at end of file diff --git a/src/Managing.Application.Abstractions/Services/IDiscordService.cs b/src/Managing.Application.Abstractions/Services/IDiscordService.cs new file mode 100644 index 0000000..e5d1a3a --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IDiscordService.cs @@ -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 traders); + Task SendBadTraders(List traders); +} diff --git a/src/Managing.Application.Abstractions/Services/IExchangeService.cs b/src/Managing.Application.Abstractions/Services/IExchangeService.cs new file mode 100644 index 0000000..9759a06 --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IExchangeService.cs @@ -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 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 GetBalance(Account account, bool isForPaperTrading = false); + Task> GetBalances(Account account, bool isForPaperTrading = false); + decimal GetPrice(Account account, Ticker ticker, DateTime date); + Task GetTrade(Account account, string order, Ticker ticker); + Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval); + Task OpenStopLoss(Account account, Ticker ticker, TradeDirection originalDirection, decimal stopLossPrice, + decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null); + Task> GetTickers(Account account, Timeframe timeframe); + Task OpenTakeProfit(Account account, Ticker ticker, TradeDirection originalDirection, decimal takeProfitPrice, + decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null); + Task ClosePosition(Account account, Position position, decimal lastPrice, bool isForPaperTrading = false); + decimal GetVolume(Account account, Ticker ticker); + Task> GetTrades(Account account, Ticker ticker); + Task CancelOrder(Account account, Ticker ticker); + decimal GetFee(Account account, bool isForPaperTrading = false); + Candle GetCandle(Account account, Ticker ticker, DateTime date); + Task GetQuantityInPosition(Account account, Ticker ticker); + Task> 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> GetOpenOrders(Account account, Ticker ticker); + Task GetTrade(string reference, string orderId, Ticker ticker); +} \ No newline at end of file diff --git a/src/Managing.Application.Abstractions/Services/IExchangeStream.cs b/src/Managing.Application.Abstractions/Services/IExchangeStream.cs new file mode 100644 index 0000000..0c19dd6 --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IExchangeStream.cs @@ -0,0 +1,10 @@ +using Managing.Common; +using Managing.Domain.Candles; + +namespace Managing.Application.Abstractions.Services; + +public interface IExchangeStream +{ + Task StartBinanceWorker(Enums.Ticker ticker, Func action); + Task StopBinanceWorker(); +} diff --git a/src/Managing.Application.Abstractions/Services/IMessengerService.cs b/src/Managing.Application.Abstractions/Services/IMessengerService.cs new file mode 100644 index 0000000..0d7817b --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IMessengerService.cs @@ -0,0 +1,19 @@ +using Managing.Domain.Statistics; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Application.Abstractions.Services; + +public interface IMessengerService +{ + 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 traders); + Task SendBadTraders(List filteredTrader); +} diff --git a/src/Managing.Application.Abstractions/Services/IStreamService.cs b/src/Managing.Application.Abstractions/Services/IStreamService.cs new file mode 100644 index 0000000..8ef92c7 --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IStreamService.cs @@ -0,0 +1,7 @@ +namespace Managing.Application.Abstractions.Services; + +public interface IStreamService +{ + Task SubscribeCandle(); + Task UnSubscribeCandle(); +} diff --git a/src/Managing.Application.Abstractions/Services/ITickerService.cs b/src/Managing.Application.Abstractions/Services/ITickerService.cs new file mode 100644 index 0000000..c1cb4ee --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/ITickerService.cs @@ -0,0 +1,8 @@ +using Managing.Common; + +namespace Managing.Application.Abstractions.Services; + +public interface ITickerService +{ + Task> GetAvailableTicker(); +} diff --git a/src/Managing.Application.Abstractions/Services/ITradaoService.cs b/src/Managing.Application.Abstractions/Services/ITradaoService.cs new file mode 100644 index 0000000..af6b2ef --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/ITradaoService.cs @@ -0,0 +1,11 @@ +using Managing.Domain.Statistics; +using Managing.Domain.Trades; + +namespace Managing.Application.Abstractions.Services; + +public interface ITradaoService +{ + Task> GetBadTrader(); + Task> GetBestTrader(); + Task> GetTrades(string address); +} diff --git a/src/Managing.Application.Abstractions/Services/ITradingService.cs b/src/Managing.Application.Abstractions/Services/ITradingService.cs new file mode 100644 index 0000000..df7cc03 --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/ITradingService.cs @@ -0,0 +1,35 @@ +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Scenarios; +using Managing.Domain.Statistics; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Application.Abstractions.Services; + +public interface ITradingService +{ + 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 GetScenarios(); + IEnumerable GetStrategies(); + void DeleteScenario(string name); + void DeleteStrategy(string name); + void DeleteScenarios(); + void DeleteStrategies(); + Position GetPositionByIdentifier(string identifier); + IEnumerable GetPositions(PositionInitiator positionInitiator); + IEnumerable GetPositions(); + IEnumerable GetPositionsByStatus(Enums.PositionStatus positionStatus); + Task ManagePosition(Account account, Position position); + void UpdateFee(TradingExchanges evm); + decimal GetFee(Account account, bool isForPaperTrading = false); + Task WatchTrader(); + IEnumerable GetTradersWatch(); +} \ No newline at end of file diff --git a/src/Managing.Application.Abstractions/Services/IUserService.cs b/src/Managing.Application.Abstractions/Services/IUserService.cs new file mode 100644 index 0000000..36349a5 --- /dev/null +++ b/src/Managing.Application.Abstractions/Services/IUserService.cs @@ -0,0 +1,9 @@ +using Managing.Domain.Users; + +namespace Managing.Application.Abstractions.Services; + +public interface IUserService +{ + Task Authenticate(string name, string address, string message, string signature); + Task GetUserByAddressAsync(string address); +} diff --git a/src/Managing.Application.Tests/BaseTests.cs b/src/Managing.Application.Tests/BaseTests.cs new file mode 100644 index 0000000..17a344d --- /dev/null +++ b/src/Managing.Application.Tests/BaseTests.cs @@ -0,0 +1,51 @@ +using Managing.Application.Abstractions.Services; +using Managing.Application.Abstractions; +using Moq; +using Managing.Domain.MoneyManagements; +using static Managing.Common.Enums; +using Managing.Domain.Accounts; + +namespace Managing.Application.Tests; + +public class BaseTests +{ + public readonly Mock _moneyManagementService; + public readonly Mock _accountService; + public readonly IExchangeService _exchangeService; + public readonly Mock _tradingService; + public readonly Account Account; + public readonly MoneyManagement MoneyManagement; + + public const string PublicAddress = "0x0425dEAb364E9121F7CA284129dA854FD5cF22eD"; + public const string PrivateKey = ""; + + public BaseTests() + { + _accountService = new Mock(); + _moneyManagementService = new Mock(); + MoneyManagement = new MoneyManagement() + { + BalanceAtRisk = 0.30m, + Leverage = 2, + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 0.008m, + TakeProfit = 0.02m, + Name = "Default MM" + }; + + _ =_moneyManagementService.Setup(m => m.GetMoneyMangement(It.IsAny())).Returns(Task.FromResult(MoneyManagement)); + + Account = new Account() + { + Exchange = TradingExchanges.Evm, + Key = PublicAddress, + Secret = PrivateKey, + }; + + _accountService.Setup(a => a.GetAccount(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(Account)); + + _tradingService = new Mock(); + _exchangeService = TradingBaseTests.GetExchangeService(); + } +} diff --git a/src/Managing.Application.Tests/BotsTests.cs b/src/Managing.Application.Tests/BotsTests.cs new file mode 100644 index 0000000..3f7400d --- /dev/null +++ b/src/Managing.Application.Tests/BotsTests.cs @@ -0,0 +1,408 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Application.Backtesting; +using Managing.Application.Bots.Base; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Scenarios; +using Moq; +using System.Collections; +using System.Diagnostics; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests +{ + public class BotsTests : BaseTests + { + private readonly IBotFactory _botFactory; + private readonly IBacktester _backtester; + private readonly string _reportPath = "D:\\BacktestingReports\\backtesting.csv"; + private string _analysePath = "D:\\BacktestingReports\\analyse"; + private readonly string _errorsPath = "D:\\BacktestingReports\\errorsAnalyse.csv"; + private readonly string _s = "|"; + private List _elapsedTimes { get; set; } + + public BotsTests() : base () + { + var backtestRepository = new Mock().Object; + var discordService = new Mock().Object; + var tradingBotLogger = TradingBaseTests.CreateTradingBotLogger(); + var backtestLogger = TradingBaseTests.CreateBacktesterLogger(); + _botFactory = new BotFactory( + _exchangeService, + tradingBotLogger, + _moneyManagementService.Object, + discordService, + _accountService.Object, + _tradingService.Object); + _backtester = new Backtester(_exchangeService, _botFactory, backtestRepository, backtestLogger); + _elapsedTimes = new List(); + } + + [Theory] + [InlineData(Ticker.BTC, Timeframe.OneDay, -100)] + public void SwingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var scenario = new Scenario("ScalpingScenario"); + var strategy = ScenarioHelpers.BuildStrategy(StrategyType.RsiDivergence, timeframe, "RsiDiv", period: 14); + scenario.AddStrategy(strategy); + + // Act + var backtestResult = _backtester.RunFlippingBotBacktest(Account, MoneyManagement, ticker, scenario, timeframe, Convert.ToDouble(days*2), 1000); + WriteCsvReport(backtestResult.GetStringReport()); + + // Assert + Assert.True(backtestResult.FinalPnl > 0); + Assert.True(backtestResult.WinRate >= 30); + Assert.True(backtestResult.GrowthPercentage > backtestResult.HodlPercentage); + } + + [Theory] + //[InlineData(Enums.Exchanges.Binance, "ADAUSDT", Timeframe.ThirtyMinutes, -5)] + //[InlineData(Enums.Exchanges.Binance, "ADAUSDT", Timeframe.FifteenMinutes, -5)] + //[InlineData(Enums.Exchanges.Binance, "SOLUSDT", Timeframe.ThirtyMinutes, -4)] + //[InlineData(Enums.Exchanges.Binance, "SOLUSDT", Timeframe.FifteenMinutes, -4)] + //[InlineData(Enums.Exchanges.Binance, "BTCUSDT", Timeframe.ThirtyMinutes, -4)] + //[InlineData(Enums.Exchanges.Binance, "BTCUSDT", Timeframe.FifteenMinutes, -4)] + [InlineData(Ticker.BTC, Timeframe.FifteenMinutes, -14)] + public void ScalpingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var scenario = new Scenario("ScalpingScenario"); + var strategy = ScenarioHelpers.BuildStrategy(StrategyType.RsiDivergence, timeframe, "RsiDiv", period: 5); + scenario.AddStrategy(strategy); + + // Act + var backtestResult = _backtester.RunScalpingBotBacktest(Account, MoneyManagement, ticker, scenario, timeframe, Convert.ToDouble(days), 1000); + //WriteCsvReport(backtestResult.GetStringReport()); + + // Assert + Assert.True(backtestResult.FinalPnl > 0); + Assert.True(backtestResult.WinRate >= 30); + Assert.True(backtestResult.GrowthPercentage > backtestResult.HodlPercentage); + } + + [Theory] + [InlineData(Ticker.BTC, Timeframe.FifteenMinutes, -8)] + public void MacdCross_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var scenario = new Scenario("ScalpingScenario"); + var strategy = ScenarioHelpers.BuildStrategy(StrategyType.MacdCross, timeframe, "RsiDiv", fastPeriods: 12, slowPeriods: 26, signalPeriods: 9); + scenario.AddStrategy(strategy); + + var moneyManagement = new MoneyManagement() + { + BalanceAtRisk = 0.05m, + Leverage = 1, + Timeframe = timeframe, + StopLoss = 0.01m, + TakeProfit = 0.02m + }; + + // Act + var backtestResult = _backtester.RunScalpingBotBacktest(Account, moneyManagement, ticker, scenario, timeframe, Convert.ToDouble(days), 1000); + WriteCsvReport(backtestResult.GetStringReport()); + + // Assert + Assert.True(backtestResult.FinalPnl > 0); + Assert.True(backtestResult.WinRate >= 30); + Assert.True(backtestResult.GrowthPercentage > backtestResult.HodlPercentage); + } + + [Theory] + [InlineData(Timeframe.FifteenMinutes, -6, StrategyType.RsiDivergenceConfirm, BotType.ScalpingBot)] + //[InlineData(Timeframe.FifteenMinutes, -6, Enums.StrategyType.RsiDivergenceConfirm, Enums.BotType.FlippingBot)] + public void GetBestPeriodRsiForDivergenceFlippingBot(Timeframe timeframe, int days, StrategyType strategyType, BotType botType) + { + var result = new List>(); + var errors = new List(); + var options = new ParallelOptions() + { + MaxDegreeOfParallelism = 4 + }; + + var periodRange = new List() { 2, 7}; + var stopLossRange = new List() { 0.005m, 0.05m, 0.005m }; + var takeProfitRange = new List() { 0.01m, 0.1m, 0.02m }; + var fileIdentifier = $"{strategyType}-{timeframe}"; + var completedTest = 0; + var totalTests = GetTotalTrades(periodRange, stopLossRange, takeProfitRange) * Enum.GetNames(typeof(Ticker)).Length; + + CleanAnalyseFile(fileIdentifier); + UpdateProgression(totalTests, completedTest); + + Parallel.ForEach((Ticker[])Enum.GetValues(typeof(Ticker)), options, ticker => { + + var candles = _exchangeService.GetCandles(Account, ticker, DateTime.Now.AddDays(Convert.ToDouble(days)), timeframe).Result; + + if (candles == null || candles.Count == 0) + return; + + Parallel.For(periodRange[0], periodRange[1], options, i => { + var scenario = new Scenario("ScalpingScenario"); + var strategy = ScenarioHelpers.BuildStrategy(strategyType, timeframe, "RsiDiv", period: i); + scenario.AddStrategy(strategy); + + // -0.5 to -5 + for (decimal s = stopLossRange[0]; s < stopLossRange[1]; s += stopLossRange[2]) + { + // +1% to +10% in 1% + for(decimal t = takeProfitRange[0]; t < takeProfitRange[1]; t += takeProfitRange[2]) + { + var moneyManagement = new MoneyManagement() + { + BalanceAtRisk = 0.05m, + Leverage = 1, + Timeframe = timeframe, + StopLoss = s, + TakeProfit = t + }; + + try + { + var timer = new Stopwatch(); + timer.Start(); + + var backtestResult = botType switch + { + BotType.SimpleBot => throw new NotImplementedException(), + BotType.ScalpingBot => _backtester.RunScalpingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000), + BotType.FlippingBot => _backtester.RunFlippingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000), + _ => throw new NotImplementedException(), + }; + timer.Stop(); + + if (backtestResult.FinalPnl > 0 + && (backtestResult.GrowthPercentage - backtestResult.HodlPercentage) > 30 + && backtestResult.Statistics.MaxDrawdown < 3) + { + var currentResult = new Tuple(ticker.ToString(), i, + backtestResult.FinalPnl, s, t, backtestResult.GrowthPercentage - backtestResult.HodlPercentage); + result.Add(currentResult); + } + + completedTest++; + UpdateProgression(totalTests, completedTest, timer.Elapsed.TotalSeconds); + } + catch (Exception ex) + { + completedTest++; + errors.Add($"{ticker}{_s}{i}{_s}{s}{_s}{t}{_s}{ex.Message}"); + } + } + } + }); + }); + + foreach (var r in result) + { + WriteCsvAnalyse($"{r.Item1}{_s}{r.Item2}{_s}{r.Item3.ToString("0.000")}{_s}{r.Item4 * 100}{_s}{r.Item5 * 100}{_s}{r.Item6}"); + } + + foreach (var e in errors) + { + WriteCsvErrors(e); + } + + var bestResult = result.OrderByDescending(b => b.Item3).FirstOrDefault(); + WriteCsvAnalyse($"Best result : {bestResult.Item1} - Rsi Period : {bestResult.Item2} - {bestResult.Item3} - SL : {bestResult.Item4}% - TP : {bestResult.Item5}%"); + + Assert.True(result.Any()); + } + + + [Theory] + [InlineData(Timeframe.OneHour, -30, StrategyType.MacdCross, BotType.FlippingBot)] + [InlineData(Timeframe.OneHour, -30, StrategyType.MacdCross, BotType.ScalpingBot)] + public void GetBestMMForMacdFlippingBot(Timeframe timeframe, int days, StrategyType strategyType, BotType botType) + { + var result = new List>(); + var errors = new List(); + var options = new ParallelOptions() + { + MaxDegreeOfParallelism = 4 + }; + + var stopLossRange = new List() { 0.005m, 0.05m, 0.005m }; + var takeProfitRange = new List() { 0.01m, 0.1m, 0.02m }; + var fileIdentifier = $"{strategyType}-{timeframe}-{botType}"; + var completedTest = 0; + var totalTests = GetTotalTradeForStopLossTakeProfit(stopLossRange, takeProfitRange) * Enum.GetNames(typeof(Ticker)).Length; + + CleanAnalyseFile(fileIdentifier); + UpdateProgression(totalTests, completedTest); + + Parallel.ForEach((Ticker[])Enum.GetValues(typeof(Ticker)), options, ticker => { + + var candles = _exchangeService.GetCandles(Account, ticker, DateTime.Now.AddDays(Convert.ToDouble(days)), timeframe).Result; + + if (candles == null || candles.Count == 0) + return; + + var scenario = new Scenario("ScalpingScenario"); + var strategy = ScenarioHelpers.BuildStrategy(strategyType, timeframe, "RsiDiv", fastPeriods: 12, slowPeriods: 26, signalPeriods: 9); + scenario.AddStrategy(strategy); + + // -0.5 to -5 + for (decimal s = stopLossRange[0]; s < stopLossRange[1]; s += stopLossRange[2]) + { + // +1% to +10% in 1% + for (decimal t = takeProfitRange[0]; t < takeProfitRange[1]; t += takeProfitRange[2]) + { + var timer = new Stopwatch(); + timer.Start(); + try + { + var moneyManagement = new MoneyManagement() + { + BalanceAtRisk = 0.05m, + Leverage = 1, + Timeframe = timeframe, + StopLoss = s, + TakeProfit = t + }; + + var backtestResult = botType switch + { + BotType.SimpleBot => throw new NotImplementedException(), + BotType.ScalpingBot => _backtester.RunScalpingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000), + BotType.FlippingBot => _backtester.RunFlippingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000), + _ => throw new NotImplementedException(), + }; + + if (backtestResult.FinalPnl > 0 + && (backtestResult.GrowthPercentage - backtestResult.HodlPercentage) > 30 + && backtestResult.Statistics.MaxDrawdown < 3) + { + var currentResult = new Tuple(ticker.ToString(), + backtestResult.FinalPnl, s, t, backtestResult.GrowthPercentage - backtestResult.HodlPercentage); + result.Add(currentResult); + } + + completedTest++; + UpdateProgression(totalTests, completedTest, timer.Elapsed.TotalSeconds); + } + catch (Exception ex) + { + completedTest++; + errors.Add($"{ticker}{_s}{s}{_s}{t}{_s}{ex.Message}"); + } + timer.Stop(); + } + } + }); + + foreach (var r in result) + { + WriteCsvAnalyse($"{r.Item1}{_s}{r.Item2.ToString("0.000")}{_s}{r.Item3 * 100}{_s}{r.Item4 * 100}{_s}{r.Item5}"); + } + + foreach (var e in errors) + { + WriteCsvErrors(e); + } + + var bestResult = result.OrderByDescending(b => b.Item3).FirstOrDefault(); + WriteCsvAnalyse($"Best result : {bestResult.Item1} - Rsi Period : {bestResult.Item2} - {bestResult.Item3} - SL : {bestResult.Item4}% - TP : {bestResult.Item5}%"); + + Assert.True(result.Any()); + } + + + private void WriteCsvReport(string line) + { + File.AppendAllLines(_reportPath, new[] { line }); + } + + private void WriteCsvAnalyse(string line, string fileIdentifier = null) + { + if (!string.IsNullOrEmpty(fileIdentifier)) + _analysePath += $"-{fileIdentifier}-{DateTime.Now.ToString("dd-MM-HH-mm")}.csv"; + + File.AppendAllLines(_analysePath , new[] { line }); + } + + private void WriteCsvErrors(string line) + { + File.AppendAllLines(_errorsPath, new[] { line }); + } + + private void CleanAnalyseFile(string fileIdentifier) + { + WriteCsvAnalyse("", fileIdentifier); + } + + private decimal GetTotalTrades(List periodRange, List stopLossRange, List takeProfitRange) + { + var stopLossRangeTotalTest = stopLossRange[1] / stopLossRange[2]; + var takeProfitRangeTotalTest = takeProfitRange[1] / takeProfitRange[2]; + + var totalTrades = GetTotalTradeForStopLossTakeProfit(stopLossRange, takeProfitRange) * stopLossRangeTotalTest * takeProfitRangeTotalTest; + return totalTrades; + } + + private decimal GetTotalTradeForStopLossTakeProfit(List stopLossRange, List takeProfitRange) + { + var stopLossRangeTotalTest = stopLossRange[1] / stopLossRange[2]; + var takeProfitRangeTotalTest = takeProfitRange[1] / takeProfitRange[2]; + + var totalTrades = stopLossRangeTotalTest * takeProfitRangeTotalTest; + return totalTrades; + } + + + private void UpdateProgression(decimal totalTest, int completedTest, double? elapsed = null) + { + var timeRemaining = ""; + if (elapsed.HasValue && completedTest > 0) + { + //_elapsedTimes.Add((elapsed.Value / completedTest) * ((double)totalTest - completedTest)); + _elapsedTimes.Add(elapsed.Value); + //var t = (_elapsedTimes.Average() / completedTest) * ((double)totalTest - completedTest); + var t = (_elapsedTimes.Average() * (double)(totalTest - completedTest)); + timeRemaining = $" Remaining time: {t} seconds - Estimated end date: {DateTime.Now.AddSeconds(t)}"; + } + + ModifyFirstRow(_analysePath, $"{completedTest}/{totalTest}{timeRemaining}"); + } + + private void ModifyFirstRow(string filepath, string newValue) + { + ArrayList rows = new ArrayList(); + + using (StreamReader reader = new StreamReader(filepath)) + { + string row = null; + + while ((row = reader.ReadLine()) != null) + { + rows.Add(row); + } + } + + // Determ if the file even contains rows. + if (rows.Count > 0) + { + // Replace the first row. + rows[0] = newValue; + } + else + { + // Add the new value because there + // where no rows found in the file. + rows.Add(newValue); + } + + // Write the modified content to the file. + using (StreamWriter writer = new StreamWriter(filepath, false)) + { + foreach (String row in rows) + { + writer.WriteLine(row); + } + } + } + } +} diff --git a/src/Managing.Application.Tests/CandleHelpersTests.cs b/src/Managing.Application.Tests/CandleHelpersTests.cs new file mode 100644 index 0000000..4151045 --- /dev/null +++ b/src/Managing.Application.Tests/CandleHelpersTests.cs @@ -0,0 +1,20 @@ +using Xunit; + +namespace Managing.Application.Tests +{ + public class CandleHelpersTests + { + [Fact] + public void Shoud_Result_Correct_Range_Date() + { + // Arrange + var expectedDate = DateTime.Now.AddMinutes(-15*5); + var currentCandleDate = DateTime.Now; + var previousCandleDate = DateTime.Now.AddMinutes(-15); + + // Act + //var result = CandleHelpers.GetRangeDateFromTimeframe + // Assert + } + } +} diff --git a/src/Managing.Application.Tests/Managing.Application.Tests.csproj b/src/Managing.Application.Tests/Managing.Application.Tests.csproj new file mode 100644 index 0000000..d91fda2 --- /dev/null +++ b/src/Managing.Application.Tests/Managing.Application.Tests.csproj @@ -0,0 +1,35 @@ + + + + net7.0 + enable + AnyCPU + AnyCPU;x64 + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/src/Managing.Application.Tests/MathHelpersTests.cs b/src/Managing.Application.Tests/MathHelpersTests.cs new file mode 100644 index 0000000..c333754 --- /dev/null +++ b/src/Managing.Application.Tests/MathHelpersTests.cs @@ -0,0 +1,24 @@ +using Managing.Core; +using Xunit; + +namespace Managing.Application.Tests +{ + public class MathHelpersTests + { + [Theory] + [InlineData(0.00010, 4)] + public void Should_Return_Correct_Precision(decimal n, int expectedValue) + { + var precision = MathHelpers.GetDecimalPlaces(n); + Assert.Equal(expectedValue, precision); + } + + [Theory] + [InlineData("0.00010", 4)] + public void Should_Return_Correct_PrecisionTest(string s, int expectedValue) + { + var precision = MathHelpers.GetDecimalPlaces(s); + Assert.Equal(expectedValue, precision); + } + } +} diff --git a/src/Managing.Application.Tests/PositionTests.cs b/src/Managing.Application.Tests/PositionTests.cs new file mode 100644 index 0000000..9c613c8 --- /dev/null +++ b/src/Managing.Application.Tests/PositionTests.cs @@ -0,0 +1,56 @@ +using Managing.Application.Trading; +using Managing.Application.Trading.Commands; +using Managing.Domain.Trades; +using Moq; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests; + +public class PositionTests : BaseTests +{ + public PositionTests() : base() + { + } + + [Fact] + public async void Should_Open_Position() + { + var command = new OpenPositionRequest( + "test", + MoneyManagement, + TradeDirection.Short, + Ticker.BTC, + PositionInitiator.User, + DateTime.UtcNow, + isForPaperTrading: false); + var handler = new OpenPositionCommandHandler( + _exchangeService, + _accountService.Object, + _tradingService.Object); + + var position = await handler.Handle(command); + + Assert.NotNull(position); + } + + [Fact] + public async void Shoud_Close_Position() + { + var openTrade = await _exchangeService.GetTrade(Account, "", Ticker.BTC); + var position = new Position("", TradeDirection.Long, Ticker.BTC, MoneyManagement, PositionInitiator.User, DateTime.UtcNow) + { + Open = openTrade + }; + var command = new ClosePositionCommand(position); + _ = _tradingService.Setup(m => m.GetPositionByIdentifier(It.IsAny())).Returns(position); + + var handler = new ClosePositionCommandHandler( + _exchangeService, + _accountService.Object, + _tradingService.Object); + + var closedPosition = await handler.Handle(command); + Assert.NotNull(closedPosition); + } +} diff --git a/src/Managing.Application.Tests/ProfitAndLossTests.cs b/src/Managing.Application.Tests/ProfitAndLossTests.cs new file mode 100644 index 0000000..11752d2 --- /dev/null +++ b/src/Managing.Application.Tests/ProfitAndLossTests.cs @@ -0,0 +1,251 @@ +using Managing.Domain.Trades; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests +{ + public class ProfitAndLossTests + { + [Theory] + [InlineData(1, 100, 110, 10)] + [InlineData(2, 100, 110, 20)] + public void Should_Return_Correct_ProfitAndLoss_Amount(decimal quantity, decimal price, decimal exitPrice, decimal expectedResult) + { + // Arrange + var init = new List>(); + init.Add(new Tuple(quantity, price)); + + var pnl = new ProfitAndLoss(init, TradeDirection.Long); + + // Act + var result = pnl.FloatingForTheoriticalExit(exitPrice); + + + // Assert + Assert.Equal(expectedResult, result); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit() + { + // Arrange + var position = GetFakeShortPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + + // Trigger Stop Loss + position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection); + + // Assert + Assert.Equal(20, position.ProfitAndLoss.Realized); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Long_Solana_failed_Position_After_Took_One_Profit() + { + // Arrange + var position = GetSolanaLongPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + + // Trigger Stop Loss + position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection); + + // Assert + Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Long_Solana_failed_Position_After_Took_One_Profit2() + { + // Arrange + var position = GetSolanaLongPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price), + new Tuple(-position.TakeProfit2.Quantity, position.StopLoss.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + + // Assert + Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit2() + { + // Arrange + var position = GetFakeShortPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price), + new Tuple(-position.TakeProfit2.Quantity, position.StopLoss.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + + // Assert + Assert.Equal(20, position.ProfitAndLoss.Realized); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit3() + { + // Arrange + var position = GetFakeShortPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + position.ProfitAndLoss.AddFill(-position.TakeProfit1.Quantity, position.TakeProfit1.Price, TradeDirection.Short); + position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Short); + + // Assert + Assert.Equal(20, position.ProfitAndLoss.Realized); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Short_Succeeded_Position_After_Two_Take_Profit() + { + // Arrange + var position = GetFakeShortPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + + // Trigger Stop Loss + position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Short); + + // Assert + Assert.Equal(120, position.ProfitAndLoss.Realized); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Long_failed_Position_After_Took_One_Profit() + { + // Arrange + var position = GetFakeLongPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + + // Trigger Stop Loss + position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Long); + + // Assert + Assert.Equal(20, position.ProfitAndLoss.Realized); + } + + [Fact] + public void Should_Return_Correct_Pnl_For_Long_Succeeded_Position_After_Two_Take_Profit() + { + // Arrange + var position = GetFakeLongPosition(); + + // Setup Open and first take profit + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) + }; + + // Act + position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); + + // Trigger Stop Loss + position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Long); + + // Assert + Assert.Equal(120, position.ProfitAndLoss.Realized); + } + + private static Position GetFakeShortPosition() + { + return new Position("FakeAccount", TradeDirection.Short, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow) + { + Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled, + TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""), + StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, + TradeType.StopMarket, Ticker.ADA, 10, 110, 1, "StopLossOrderId", ""), + TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, + TradeType.TakeProfitLimit, Ticker.ADA, 6, 90, 1, "TakeProfit1OrderId", ""), + TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, + TradeType.TakeProfitLimit, Ticker.ADA, 4, 85, 1, "TakeProfit1OrderId", "") + }; + } + + private static Position GetSolanaLongPosition() + { + return new Position("FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow) + { + Open = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.Filled, + TradeType.Market, Ticker.ADA, (decimal)6.0800904000245037980887037491, (decimal)81.6200, 1, "OpenOrderId", ""), + StopLoss = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, + TradeType.StopMarket, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)79.987600, 1, "StopLossOrderId", ""), + TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, + TradeType.TakeProfitLimit, Ticker.ADA, (decimal)2.4320361600098015192354814996, (decimal)85.701000, 1, "TakeProfit1OrderId", ""), + TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, + TradeType.TakeProfitLimit, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)89.782000, 1, "TakeProfit1OrderId", "") + }; + } + + private static Position GetFakeLongPosition() + { + return new Position("FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow) + { + Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled, + TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""), + StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, + TradeType.StopMarket, Ticker.ADA, 10, 90, 1, "StopLossOrderId", ""), + TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, + TradeType.TakeProfitLimit, Ticker.ADA, 6, 110, 1, "TakeProfit1OrderId", ""), + TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, + TradeType.TakeProfitLimit, Ticker.ADA, 4, 115, 1, "TakeProfit1OrderId", "") + }; + } + } +} diff --git a/src/Managing.Application.Tests/RiskHelpersTests.cs b/src/Managing.Application.Tests/RiskHelpersTests.cs new file mode 100644 index 0000000..6bdf5e0 --- /dev/null +++ b/src/Managing.Application.Tests/RiskHelpersTests.cs @@ -0,0 +1,62 @@ +using Managing.Domain.MoneyManagements; +using Managing.Domain.Shared.Helpers; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests +{ + public class RiskHelpersTests + { + private readonly MoneyManagement _moneyManagement; + public RiskHelpersTests() + { + _moneyManagement = new MoneyManagement() + { + BalanceAtRisk = 0.05m, + Leverage = 1, + Timeframe = Timeframe.OneDay, + StopLoss = 0.008m, + TakeProfit = 0.02m + }; + } + + [Theory] + [InlineData(1000, 992)] + public void Should_Return_Correct_Stop_Loss_Price_For_Long(decimal price, decimal expectedResult) + { + var stopLossPrice = RiskHelpers.GetStopLossPrice(TradeDirection.Long, price, _moneyManagement); + Assert.Equal(expectedResult, stopLossPrice); + } + + [Theory] + [InlineData(1000, 1008)] + public void Should_Return_Correct_Stop_Loss_Price_For_Short(decimal price, decimal expectedResult) + { + var stopLossPrice = RiskHelpers.GetStopLossPrice(TradeDirection.Short, price, _moneyManagement); + Assert.Equal(expectedResult, stopLossPrice); + } + + [Theory] + [InlineData(1000, 980)] + public void Should_Return_Correct_Take_Profit_Price_For_Short(decimal price, decimal expectedResult) + { + var stopLossPrice = RiskHelpers.GetTakeProfitPrice(TradeDirection.Short, price, _moneyManagement); + Assert.Equal(expectedResult, stopLossPrice); + } + + [Theory] + [InlineData(1000, 1020)] + public void Should_Return_Correct_Take_Profit_Price_For_Long(decimal price, decimal expectedResult) + { + var stopLossPrice = RiskHelpers.GetTakeProfitPrice(TradeDirection.Long, price, _moneyManagement); + Assert.Equal(expectedResult, stopLossPrice); + } + + [Fact] + public void Test() + { + const decimal test = (decimal)34523.4352; + Assert.Equal((decimal)34523.4, Math.Round(test, 1)); + } + } +} diff --git a/src/Managing.Application.Tests/StrategyTests.cs b/src/Managing.Application.Tests/StrategyTests.cs new file mode 100644 index 0000000..dd074a6 --- /dev/null +++ b/src/Managing.Application.Tests/StrategyTests.cs @@ -0,0 +1,212 @@ +using Managing.Application.Abstractions.Services; +using Managing.Domain.Accounts; +using Managing.Domain.Strategies; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests +{ + public class StrategyTests + { + private readonly IExchangeService _exchangeService; + + public StrategyTests() + { + _exchangeService = TradingBaseTests.GetExchangeService(); + } + + [Theory] + [InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)] + [InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)] + [InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)] + public void Shoud_Return_Signal_On_Rsi_BullishDivergence2(TradingExchanges exchange, Ticker ticker, + Timeframe timeframe) + { + var account = GetAccount(exchange); + // Arrange + var rsiStrategy = new RSIDivergenceStrategy("unittest", timeframe, 5); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(-50), timeframe).Result; + var resultSignal = new List(); + + // Act + foreach (var candle in candles) + { + rsiStrategy.Candles.Add(candle); + var signals = rsiStrategy.Run(); + } + + if (rsiStrategy.Signals != null && rsiStrategy.Signals.Count > 0) + resultSignal.AddRange(rsiStrategy.Signals); + + // Assert + Assert.IsType>(resultSignal); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long); + } + + private static Account GetAccount(TradingExchanges exchange) + { + return new Account() + { + Exchange = exchange + }; + } + + [Theory] + [InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)] + public void Shoud_Return_Signal_On_Rsi_BearishDivergence(TradingExchanges exchange, Ticker ticker, + Timeframe timeframe) + { + // Arrange + var account = GetAccount(exchange); + var rsiStrategy = new RSIDivergenceStrategy("unittest", timeframe, 5); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(-50), timeframe).Result; + var resultSignal = new List(); + + // Act + foreach (var candle in candles) + { + rsiStrategy.Candles.Add(candle); + var signals = rsiStrategy.Run(); + } + + if (rsiStrategy.Signals != null && rsiStrategy.Signals.Count > 0) + resultSignal.AddRange(rsiStrategy.Signals); + + // Assert + Assert.IsType>(resultSignal); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short); + } + + + [Theory] + [InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)] + public void Shoud_Return_Signal_On_Macd_Cross(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var account = GetAccount(exchange); + var rsiStrategy = new MACDCrossStrategy("unittest", timeframe, 12, 26, 9); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result; + var resultSignal = new List(); + + // Act + foreach (var candle in candles) + { + rsiStrategy.Candles.Add(candle); + var signals = rsiStrategy.Run(); + } + + if (rsiStrategy.Signals != null && rsiStrategy.Signals.Count > 0) + resultSignal.AddRange(rsiStrategy.Signals); + + // Assert + Assert.IsType>(resultSignal); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long); + } + + [Theory] + [InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)] + public void Shoud_Return_Signal_On_SuperTrend(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var account = GetAccount(exchange); + var superTrendStrategy = new SuperTrendStrategy("unittest", timeframe, 10, 3); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result; + var resultSignal = new List(); + + // Act + foreach (var candle in candles) + { + superTrendStrategy.Candles.Add(candle); + var signals = superTrendStrategy.Run(); + } + + if (superTrendStrategy.Signals != null && superTrendStrategy.Signals.Count > 0) + resultSignal.AddRange(superTrendStrategy.Signals); + + // Assert + Assert.IsType>(resultSignal); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long); + } + + [Theory] + [InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)] + public void Shoud_Return_Signal_On_ChandelierExist(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var account = GetAccount(exchange); + var chandelierExitStrategy = new ChandelierExitStrategy("unittest", timeframe, 22, 3); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result; + var resultSignal = new List(); + + // Act + foreach (var candle in candles) + { + chandelierExitStrategy.Candles.Add(candle); + var signals = chandelierExitStrategy.Run(); + } + + if (chandelierExitStrategy.Signals != null && chandelierExitStrategy.Signals.Count > 0) + resultSignal.AddRange(chandelierExitStrategy.Signals); + + // Assert + Assert.IsType>(resultSignal); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long); + } + + [Theory] + [InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)] + public void Shoud_Return_Signal_On_EmaTrend(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var account = GetAccount(exchange); + var emaTrendSrategy = new EmaTrendStrategy("unittest", timeframe, 200); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result; + var resultSignal = new List(); + + // Act + foreach (var candle in candles) + { + emaTrendSrategy.Candles.Add(candle); + var signals = emaTrendSrategy.Run(); + } + + if (emaTrendSrategy.Signals != null && emaTrendSrategy.Signals.Count > 0) + resultSignal.AddRange(emaTrendSrategy.Signals); + + // Assert + Assert.IsType>(resultSignal); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long); + } + + + [Theory] + [InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)] + public void Shoud_Return_Signal_On_StochRsi(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days) + { + // Arrange + var account = GetAccount(exchange); + var stochRsiStrategy = new StochRsiTrendStrategy("unittest", timeframe, 14, 14, 3, 1); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result; + var resultSignal = new List(); + + // Act + foreach (var candle in candles) + { + stochRsiStrategy.Candles.Add(candle); + var signals = stochRsiStrategy.Run(); + } + + if (stochRsiStrategy.Signals != null && stochRsiStrategy.Signals.Count > 0) + resultSignal.AddRange(stochRsiStrategy.Signals); + + // Assert + Assert.IsType>(resultSignal); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short); + Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long); + } + } +} diff --git a/src/Managing.Application.Tests/TradingBaseTests.cs b/src/Managing.Application.Tests/TradingBaseTests.cs new file mode 100644 index 0000000..17a9802 --- /dev/null +++ b/src/Managing.Application.Tests/TradingBaseTests.cs @@ -0,0 +1,85 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Application.Backtesting; +using Managing.Application.Bots; +using Managing.Infrastructure.Databases; +using Managing.Infrastructure.Databases.InfluxDb; +using Managing.Infrastructure.Databases.InfluxDb.Models; +using Managing.Infrastructure.Evm; +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Infrastructure.Evm.Services; +using Managing.Infrastructure.Evm.Subgraphs; +using Managing.Infrastructure.Exchanges; +using Managing.Infrastructure.Exchanges.Abstractions; +using Managing.Infrastructure.Exchanges.Exchanges; +using Microsoft.Extensions.Logging; +using Moq; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests +{ + public static class TradingBaseTests + { + public static IExchangeService GetExchangeService() + { + ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory(); + + var ChainlinkGmx = new ChainlinkGmx(SubgraphService.GetSubgraphClient(SubgraphProvider.ChainlinkGmx)); + var Chainlink = new Chainlink(SubgraphService.GetSubgraphClient(SubgraphProvider.ChainlinkPrice)); + var GbcFeed = new Gbc(SubgraphService.GetSubgraphClient(SubgraphProvider.Gbc)); + + var Subgraphs = new List + { + ChainlinkGmx, + Chainlink, + GbcFeed + }; + var evmManager = new EvmManager(Subgraphs); + var evmProcessor = new EvmProcessor(new Mock>().Object, evmManager); + + var exchangeProcessors = new List() + { + //new Mock().Object, + //new Mock().Object, + evmProcessor + }; + + return new ExchangeService(loggerFactory.CreateLogger(), GetCandleRepository(), exchangeProcessors); + } + + public static ILogger CreateTradingBotLogger() + { + ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory(); + + return loggerFactory.CreateLogger(); + } + + public static ILogger CreateBacktesterLogger() + { + ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory(); + + return loggerFactory.CreateLogger(); + } + + public static ILogger CreateCandleRepositoryLogger() + { + ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory(); + + return loggerFactory.CreateLogger(); + } + + public static ICandleRepository GetCandleRepository() + { + var settings = new InfluxDbSettings() + { + Url = "http://localhost:8086/", + Token = "6b-OjFNaZRprYroZEx8zeLScvPqvOp9la1lEksXl8xRT0d96UyuN18iKpB6jKYFt8JJEX1NaxVMXhk-Sgy8sgg==", + Organization = "managing-org" + }; + var influxdb = new InfluxDbRepository(settings); + var candleRepository = new CandleRepository(influxdb, CreateCandleRepositoryLogger()); + + return candleRepository; + } + } +} diff --git a/src/Managing.Application.Tests/WorkflowTests.cs b/src/Managing.Application.Tests/WorkflowTests.cs new file mode 100644 index 0000000..8961bc2 --- /dev/null +++ b/src/Managing.Application.Tests/WorkflowTests.cs @@ -0,0 +1,54 @@ +using Managing.Application.Workflows.Flows.Feeds; +using Managing.Domain.Workflows; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests; + +public class WorkflowTests : BaseTests +{ + [Fact] + public async void Should_Create_Workflow_with_Feed_Ticker_Flow() + { + // Arrange + var workflow = new Workflow + { + Name = "Bot trading", + Usage = WorkflowUsage.Trading, + Description = "Basic trading Workflow", + Flows = new List() + }; + + // var rsiDivFlow = new RsiDiv() + // { + // Parameters = "{\"Period\": 14,\"Timeframe\":1}", + // Children = new List(), + // }; + + // var tickerFlow = new FeedTicker(_exchangeService) + // { + // Parameters = "{\"Exchange\": 3,\"Ticker\":9,\"Timeframe\":1}", + // Children = new List() + // { + // rsiDivFlow + // } + // }; + + // workflow.Flows.Add(tickerFlow); + + // Act + await workflow.Execute(); + + // Assert + foreach (var f in workflow.Flows) + { + Assert.False(string.IsNullOrEmpty(f.Output)); + } + Assert.NotNull(workflow); + Assert.NotNull(workflow.Flows); + Assert.Single(workflow.Flows); + Assert.Equal("Feed Ticker", workflow.Name); + Assert.Equal(WorkflowUsage.Trading, workflow.Usage); + Assert.Equal("Basic trading Workflow", workflow.Description); + } +} diff --git a/src/Managing.Application.Workers/Abstractions/IPricesService.cs b/src/Managing.Application.Workers/Abstractions/IPricesService.cs new file mode 100644 index 0000000..5b6cae9 --- /dev/null +++ b/src/Managing.Application.Workers/Abstractions/IPricesService.cs @@ -0,0 +1,8 @@ +using static Managing.Common.Enums; + +namespace Managing.Application.Workers.Abstractions; + +public interface IPricesService +{ + Task UpdatePrice(TradingExchanges exchange, Ticker ticker, Timeframe timeframe); +} diff --git a/src/Managing.Application.Workers/Abstractions/IStatisticService.cs b/src/Managing.Application.Workers/Abstractions/IStatisticService.cs new file mode 100644 index 0000000..8ab8d08 --- /dev/null +++ b/src/Managing.Application.Workers/Abstractions/IStatisticService.cs @@ -0,0 +1,19 @@ +using Managing.Common; +using Managing.Domain.Statistics; +using Managing.Domain.Trades; + +namespace Managing.Application.Workers.Abstractions; + +public interface IStatisticService +{ + List GetBadTraders(); + List GetBestTraders(); + SpotlightOverview GetLastSpotlight(DateTime dateTime); + IList GetLastTopVolumeTicker(); + Task> GetLeadboardPositons(); + IList GetTickers(); + Task UpdateLeaderboard(); + Task UpdateNoobiesboard(); + Task UpdateSpotlight(); + Task UpdateTopVolumeTicker(Enums.TradingExchanges exchange, int top); +} diff --git a/src/Managing.Application.Workers/Abstractions/IWorkerService.cs b/src/Managing.Application.Workers/Abstractions/IWorkerService.cs new file mode 100644 index 0000000..7063bd3 --- /dev/null +++ b/src/Managing.Application.Workers/Abstractions/IWorkerService.cs @@ -0,0 +1,15 @@ +using Managing.Domain.Workers; +using static Managing.Common.Enums; + +namespace Managing.Application.Workers.Abstractions; + +public interface IWorkerService +{ + Task DisableWorker(WorkerType workerType); + Task EnableWorker(WorkerType workerType); + Task GetWorker(WorkerType workerType); + IEnumerable GetWorkers(); + Task InsertWorker(WorkerType workerType, TimeSpan delay); + Task ToggleWorker(WorkerType workerType); + Task UpdateWorker(WorkerType workerType, int executionCount); +} diff --git a/src/Managing.Application.Workers/Managing.Application.Workers.csproj b/src/Managing.Application.Workers/Managing.Application.Workers.csproj new file mode 100644 index 0000000..75eff83 --- /dev/null +++ b/src/Managing.Application.Workers/Managing.Application.Workers.csproj @@ -0,0 +1,18 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + diff --git a/src/Managing.Application.Workers/PricesService.cs b/src/Managing.Application.Workers/PricesService.cs new file mode 100644 index 0000000..77a1b7d --- /dev/null +++ b/src/Managing.Application.Workers/PricesService.cs @@ -0,0 +1,64 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Application.Workers.Abstractions; +using Managing.Domain.Candles; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Application.Workers; + +public class PricesService : IPricesService +{ + private readonly ILogger _logger; + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + private readonly ICandleRepository _candleRepository; + + public PricesService( + ILogger logger, + IExchangeService exchangeService, + ICandleRepository candleRepository, + IAccountService accountService) + { + _logger = logger; + _exchangeService = exchangeService; + _candleRepository = candleRepository; + _accountService = accountService; + } + + public async Task UpdatePrice(TradingExchanges exchange, Ticker ticker, Timeframe timeframe) + { + try + { + var account = _accountService.GetAccounts(false, false).FirstOrDefault(a => a.Exchange == exchange); + + if (account == null) + throw new Exception($"Enable to found account for exchange {exchange}"); + + var lastCandles = await _candleRepository.GetCandles(exchange, ticker, timeframe, DateTime.UtcNow.AddDays(-2)); + var lastCandle = lastCandles.LastOrDefault(); + var startDate = lastCandle != null ? lastCandle.Date : CandleExtensions.GetPreloadSinceFromTimeframe(timeframe); + var newCandles = await _exchangeService.GetCandles(account, ticker, startDate, timeframe); + + var candles = !lastCandles.Any() ? newCandles : newCandles.Where(c => c.Date > lastCandle?.Date); + var candlesInserted = 0; + + foreach (var newCandle in candles) + { + if (lastCandle == null || newCandle.Date > lastCandle.Date) + { + _candleRepository.InsertCandle(newCandle); + candlesInserted++; + } + } + + if (candlesInserted > 0) + _logger.LogInformation($"[{exchange}][{ticker}][{timeframe}] New candles inserted : {candlesInserted}"); + + } + catch (Exception ex) + { + _logger.LogError($"[{exchange}][{ticker}][{timeframe}] Error : {ex.Message} | {ex.StackTrace}"); + } + } +} \ No newline at end of file diff --git a/src/Managing.Application.Workers/StatisticService.cs b/src/Managing.Application.Workers/StatisticService.cs new file mode 100644 index 0000000..6600beb --- /dev/null +++ b/src/Managing.Application.Workers/StatisticService.cs @@ -0,0 +1,304 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Application.Workers.Abstractions; +using Managing.Domain.Accounts; +using Managing.Domain.Candles; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Scenarios; +using Managing.Domain.Shared.Helpers; +using Managing.Domain.Statistics; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Application.Workers; + +public class StatisticService : IStatisticService +{ + private readonly IStatisticRepository _statisticRepository; + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + private readonly IEvmManager _evmManager; + private readonly ITradingService _tradingService; + private readonly IBacktester _backtester; + private readonly ITradaoService _tradaoService; + private readonly IMessengerService _messengerService; + private readonly ILogger _logger; + + public StatisticService( + IExchangeService exchangeService, + IAccountService accountService, + ILogger logger, + IStatisticRepository statisticRepository, + IEvmManager evmManager, + ITradingService tradingService, + IBacktester backtester, + ITradaoService tradaoService, + IMessengerService messengerService) + { + _exchangeService = exchangeService; + _accountService = accountService; + _logger = logger; + _statisticRepository = statisticRepository; + _evmManager = evmManager; + _tradingService = tradingService; + _backtester = backtester; + _tradaoService = tradaoService; + _messengerService = messengerService; + } + + public async Task UpdateTopVolumeTicker(TradingExchanges exchange, int top) + { + var account = _accountService.GetAccounts(false, false).FirstOrDefault(a => a.Exchange == exchange); + var date = DateTime.UtcNow; + + if (account == null) + throw new Exception($"Enable to found account for exchange {exchange}"); + + var lastTop = GetLastTopVolumeTicker(); + + if (lastTop != null && lastTop.Count > 0) + { + _logger.LogInformation($"A top of {lastTop.Count} already exist for the current rage"); + return; + } + + var volumeTickers = new Dictionary(); + foreach (var ticker in (Ticker[])Enum.GetValues(typeof(Ticker))) + { + var volume = _exchangeService.GetVolume(account, ticker); + var price = _exchangeService.GetPrice(account, ticker, date); + volumeTickers.Add(ticker, volume * price); + } + + var currentTop = volumeTickers.OrderByDescending(v => v.Value).Take(top).ToList(); + + for (int rank = 0; rank < currentTop.Count; rank++) + { + var dto = new TopVolumeTicker() + { + Date = date, + Rank = rank + 1, + Ticker = currentTop[rank].Key, + Volume = currentTop[rank].Value, + Exchange = exchange + }; + + await _statisticRepository.InsertTopVolumeTicker(dto); + } + } + + public IList GetLastTopVolumeTicker() + { + var from = DateTime.UtcNow.AddDays(-1); + return _statisticRepository.GetTopVolumeTickers(from); + } + + public IList GetTickers() + { + return _evmManager.GetAvailableTicker().Result; + } + + public async Task UpdateSpotlight() + { + var scenarios = _tradingService.GetScenarios(); + var account = _accountService.GetAccounts(false, false).FirstOrDefault(a => a.Exchange == TradingExchanges.Evm); + + if (account == null) + throw new Exception($"Enable to found default account"); + + var overview = GetLastSpotlight(DateTime.Now.AddMinutes(-20)); + + if (overview != null) + { + if(overview.Spotlights.Count < overview.ScenarioCount) + { + _logger.LogInformation($"Spotlights not up to date. {overview.Spotlights.Count}/{overview.ScenarioCount}"); + } + else + { + _logger.LogInformation("No need to update spotlights"); + return; + } + } + else + { + overview = new SpotlightOverview + { + Spotlights = new List(), + DateTime = DateTime.Now, + Identifier = Guid.NewGuid(), + ScenarioCount = scenarios.Count(), + }; + await _statisticRepository.SaveSpotligthtOverview(overview); + } + + var tickers = GetTickers(); + + foreach (var scenario in scenarios) + { + if (overview.Spotlights.Any(s => s.Scenario.Name == scenario.Name)) + continue; + + var spotlight = new Spotlight + { + TickerSignals = new List(), + Scenario = scenario + }; + + var options = new ParallelOptions() + { + MaxDegreeOfParallelism = 2 + }; + + _ = Parallel.ForEach(tickers, options, ticker => + { + spotlight.TickerSignals.Add(new TickerSignal + { + Ticker = ticker, + FiveMinutes = GetSignals(account, scenario, ticker, Timeframe.FiveMinutes), + FifteenMinutes = GetSignals(account, scenario, ticker, Timeframe.FifteenMinutes), + OneHour = GetSignals(account, scenario, ticker, Timeframe.OneHour), + FourHour = GetSignals(account, scenario, ticker, Timeframe.FourHour), + OneDay = GetSignals(account, scenario, ticker, Timeframe.OneDay) + }); + }); + + overview.Spotlights.Add(spotlight); + _statisticRepository.UpdateSpotlightOverview(overview); + } + + overview.DateTime = DateTime.Now; + _statisticRepository.UpdateSpotlightOverview(overview); + } + + private List GetSignals(Account account, Scenario scenario, Ticker ticker, Timeframe timeframe) + { + try + { + var moneyManagement = new MoneyManagement() + { + BalanceAtRisk = 0.05m, + Leverage = 1, + Timeframe = timeframe, + StopLoss = 0.008m, + TakeProfit = 0.02m + }; + + var backtest = _backtester.RunScalpingBotBacktest( + account, + moneyManagement, + ticker, + scenario, + timeframe, + CandleExtensions.GetMinimalDays(timeframe), + 1000, + isForWatchingOnly: true); + + return backtest.Signals; + } + catch (Exception ex) + { + _logger.LogError("Backtest cannot be run", ex); + } + + return null; + } + + public SpotlightOverview GetLastSpotlight(DateTime dateTime) + { + var overviews = _statisticRepository.GetSpotlightOverviews(dateTime); + + if (overviews.Any()) + { + return overviews.OrderBy(o => o.DateTime).Last(); + } + + return null; + } + + public List GetBestTraders() + { + return _statisticRepository.GetBestTraders(); + } + + public List GetBadTraders() + { + return _statisticRepository.GetBadTraders(); + } + + public async Task> GetLeadboardPositons() + { + var customWatchAccount = _tradingService.GetTradersWatch(); + var trades = new List(); + + foreach (var trader in customWatchAccount) + { + trades.AddRange(await _tradaoService.GetTrades(trader.Address)); + } + + return trades; + } + + public async Task UpdateLeaderboard() + { + var previousBestTraders = _statisticRepository.GetBestTraders(); + var lastBestTrader = (await _tradaoService.GetBestTrader()).FindGoodTrader(); + + // Update / Insert best trader + foreach (var trader in lastBestTrader) + { + if (previousBestTraders.Exists((p) => p.Address == trader.Address)) + { + _statisticRepository.UpdateBestTrader(trader); + } + else + { + await _statisticRepository.InsertBestTrader(trader); + } + } + + // Remove trader that wasnt good enough + foreach (var trader in previousBestTraders) + { + if (!lastBestTrader.Exists((t) => t.Address == trader.Address)) + { + await _statisticRepository.RemoveBestTrader(trader); + } + } + + await _messengerService.SendBestTraders(lastBestTrader); + } + + public async Task UpdateNoobiesboard() + { + var previousBadTraders = _statisticRepository.GetBadTraders(); + var lastBadTrader = (await _tradaoService.GetBadTrader()).FindBadTrader(); + + // Update / Insert best trader + foreach (var trader in lastBadTrader) + { + if (previousBadTraders.Exists((p) => p.Address == trader.Address)) + { + _statisticRepository.UpdateBadTrader(trader); + } + else + { + await _statisticRepository.InsertBadTrader(trader); + } + } + + // Remove trader that wasnt good enough + foreach (var trader in previousBadTraders) + { + if (!lastBadTrader.Exists((t) => t.Address == trader.Address)) + { + await _statisticRepository.RemoveBadTrader(trader); + } + } + + await _messengerService.SendBadTraders(lastBadTrader); + } +} diff --git a/src/Managing.Application.Workers/WorkerService.cs b/src/Managing.Application.Workers/WorkerService.cs new file mode 100644 index 0000000..f46fdfc --- /dev/null +++ b/src/Managing.Application.Workers/WorkerService.cs @@ -0,0 +1,69 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Workers.Abstractions; +using Managing.Common; +using Managing.Domain.Workers; + +namespace Managing.Application.Workers; + +public class WorkerService : IWorkerService +{ + private readonly IWorkerRepository _workerRepository; + + public WorkerService(IWorkerRepository workerRepository) + { + _workerRepository = workerRepository; + } + + public async Task GetWorker(Enums.WorkerType workerType) + { + return await _workerRepository.GetWorkerAsync(workerType); + } + + public async Task InsertWorker(Enums.WorkerType workerType, TimeSpan delay) + { + var worker = new Worker() + { + WorkerType = workerType, + StartTime = DateTime.Now, + LastRunTime = null, + ExecutionCount = 0, + Delay = delay + }; + await _workerRepository.InsertWorker(worker); + } + + public async Task UpdateWorker(Enums.WorkerType workerType, int executionCount) + { + await _workerRepository.UpdateWorker(workerType, executionCount); + } + + public async Task DisableWorker(Enums.WorkerType workerType) + { + await _workerRepository.DisableWorker(workerType); + } + + public async Task EnableWorker(Enums.WorkerType workerType) + { + await _workerRepository.EnableWorker(workerType); + } + + public IEnumerable GetWorkers() + { + return _workerRepository.GetWorkers(); + } + + public async Task ToggleWorker(Enums.WorkerType workerType) + { + var worker = await GetWorker(workerType); + if (worker.IsActive) + { + _ = _workerRepository.DisableWorker(workerType); + return false; + } + else + { + _ = _workerRepository.EnableWorker(workerType); + return true; + } + } +} diff --git a/src/Managing.Application/Abstractions/IBotFactory.cs b/src/Managing.Application/Abstractions/IBotFactory.cs new file mode 100644 index 0000000..d29fa0d --- /dev/null +++ b/src/Managing.Application/Abstractions/IBotFactory.cs @@ -0,0 +1,16 @@ +using Managing.Domain.Bots; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Workflows; +using static Managing.Common.Enums; + +namespace Managing.Application.Abstractions +{ + public interface IBotFactory + { + ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly); + ITradingBot CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly); + ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly); + ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly); + IBot CreateSimpleBot(string botName, Workflow workflow); + } +} diff --git a/src/Managing.Application/Abstractions/ICacheService.cs b/src/Managing.Application/Abstractions/ICacheService.cs new file mode 100644 index 0000000..445b6e5 --- /dev/null +++ b/src/Managing.Application/Abstractions/ICacheService.cs @@ -0,0 +1,12 @@ +namespace Managing.Application.Abstractions +{ + public interface ICacheService + { + string SaveValue(string name, string value); + string GetValue(string key); + void RemoveValue(string key); + T GetOrSave(string name, Func action, TimeSpan slidingExpiration); + T GetValue(string key); + void SaveValue(string name, T value, TimeSpan slidingExpiration); + } +} diff --git a/src/Managing.Application/Abstractions/ICommandHandler.cs b/src/Managing.Application/Abstractions/ICommandHandler.cs new file mode 100644 index 0000000..6dbadbe --- /dev/null +++ b/src/Managing.Application/Abstractions/ICommandHandler.cs @@ -0,0 +1,7 @@ +namespace Managing.Application.Abstractions +{ + public interface ICommandHandler + { + Task Handle(T request); + } +} \ No newline at end of file diff --git a/src/Managing.Application/Abstractions/IFlowFactory.cs b/src/Managing.Application/Abstractions/IFlowFactory.cs new file mode 100644 index 0000000..a7c6e8d --- /dev/null +++ b/src/Managing.Application/Abstractions/IFlowFactory.cs @@ -0,0 +1,9 @@ +using Managing.Domain.Workflows; +using Managing.Domain.Workflows.Synthetics; + +namespace Managing.Application.Abstractions; + +public interface IFlowFactory +{ + IFlow BuildFlow(SyntheticFlow request); +} diff --git a/src/Managing.Application/Abstractions/IMoneyManagementService.cs b/src/Managing.Application/Abstractions/IMoneyManagementService.cs new file mode 100644 index 0000000..18a4f18 --- /dev/null +++ b/src/Managing.Application/Abstractions/IMoneyManagementService.cs @@ -0,0 +1,13 @@ +using Managing.Domain.MoneyManagements; + +namespace Managing.Application.Abstractions +{ + public interface IMoneyManagementService + { + Task CreateOrUpdateMoneyManagement(MoneyManagement request); + Task GetMoneyMangement(string name); + IEnumerable GetMoneyMangements(); + bool DeleteMoneyManagement(string name); + bool DeleteMoneyManagements(); + } +} diff --git a/src/Managing.Application/Abstractions/IScenarioService.cs b/src/Managing.Application/Abstractions/IScenarioService.cs new file mode 100644 index 0000000..d9cbd1b --- /dev/null +++ b/src/Managing.Application/Abstractions/IScenarioService.cs @@ -0,0 +1,29 @@ +using Managing.Domain.Scenarios; +using Managing.Domain.Strategies; +using static Managing.Common.Enums; + +namespace Managing.Application.Abstractions +{ + public interface IScenarioService + { + IEnumerable GetScenarios(); + Scenario CreateScenario(string name, List strategies); + IEnumerable GetStrategies(); + bool DeleteStrategy(string name); + bool DeleteScenario(string name); + Scenario GetScenario(string name); + Strategy CreateStrategy(StrategyType type, + 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); + bool DeleteStrategies(); + bool DeleteScenarios(); + } +} diff --git a/src/Managing.Application/Abstractions/ISettingsService.cs b/src/Managing.Application/Abstractions/ISettingsService.cs new file mode 100644 index 0000000..bb6a816 --- /dev/null +++ b/src/Managing.Application/Abstractions/ISettingsService.cs @@ -0,0 +1,7 @@ +namespace Managing.Application.Abstractions; + +public interface ISettingsService +{ + bool SetupSettings(); + Task ResetSettings(); +} diff --git a/src/Managing.Application/Abstractions/ITaskCache.cs b/src/Managing.Application/Abstractions/ITaskCache.cs new file mode 100644 index 0000000..07e6e5b --- /dev/null +++ b/src/Managing.Application/Abstractions/ITaskCache.cs @@ -0,0 +1,48 @@ +namespace Managing.Application.Abstractions +{ + public interface ITaskCache + { + /// + /// Return from the cache the value for the given key. If value is already present in cache, + /// that value will be returned. Otherwise value is first generated with the given method. + /// + /// Return value can be a completed or running task-object. If the task-object is completed, + /// it has run succesfully to completion. Most often when a running task is returned, + /// it is the task returned by the function the caller has given as a parameter, but the + /// returned task might also have a different origin (from another call to this same method). + /// If the cache contains a task that will end up throwing an exception in the future, the same + /// task instance is returned to all the callers of this method. This means that any given + /// caller of this method should anticipate the type of exceptions that could be thrown from + /// the updateFunc used by any of the caller of this method. + /// + /// To prevent the problem described above, as a convention, all the call sites of his method + /// (if more than one) should use the same updateFunc-parameter and also be prepared for the + /// exceptions that the the updateFunc could throw. + /// + /// Type of the value. + /// Key that matches the wanted return value. + /// Function that is run only if a value for the given key is not already present in the cache. + /// Returned task-object can be completed or running. Note that the task might result in exception. + Task AddOrGetExisting(string key, Func> valueFactory); + + /// + /// Invalidate the value for the given key, if value exists. + /// + /// + void Invalidate(string key); + + /// + /// Does the cache alrealy contain a value for the key. + /// + /// + /// + bool Contains(string key); + + /// + /// Empties the cache from all entries. + /// + void Clear(); + List GetCache(); + T Get(string name); + } +} diff --git a/src/Managing.Application/Abstractions/ITradingBot.cs b/src/Managing.Application/Abstractions/ITradingBot.cs new file mode 100644 index 0000000..a62d70d --- /dev/null +++ b/src/Managing.Application/Abstractions/ITradingBot.cs @@ -0,0 +1,33 @@ +using Managing.Domain.Bots; +using Managing.Domain.Candles; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Application.Abstractions +{ + public interface ITradingBot : IBot + { + HashSet Signals { get; set; } + List Positions { get; set; } + HashSet Candles { get; set; } + Timeframe Timeframe { get; set; } + HashSet Strategies { get; set; } + Ticker Ticker { get; } + string Scenario { get; } + string AccountName { get; } + bool IsForWatchingOnly { get; set; } + MoneyManagement MoneyManagement { get; set; } + BotType BotType { get; set; } + Dictionary WalletBalances { get; set; } + + + Task Run(); + Task ToggleIsForWatchOnly(); + int GetWinRate(); + decimal GetProfitAndLoss(); + decimal GetTotalFees(); + void LoadStrategies(IEnumerable strategies); + } +} diff --git a/src/Managing.Application/Abstractions/IWorkflowService.cs b/src/Managing.Application/Abstractions/IWorkflowService.cs new file mode 100644 index 0000000..18165d4 --- /dev/null +++ b/src/Managing.Application/Abstractions/IWorkflowService.cs @@ -0,0 +1,12 @@ +using Managing.Domain.Workflows; +using Managing.Domain.Workflows.Synthetics; + +namespace Managing.Application.Abstractions; + +public interface IWorkflowService +{ + bool DeleteWorkflow(string name); + Task> GetAvailableFlows(); + IEnumerable GetWorkflows(); + Task InsertOrUpdateWorkflow(SyntheticWorkflow workflowRequest); +} diff --git a/src/Managing.Application/Accounts/AccountService.cs b/src/Managing.Application/Accounts/AccountService.cs new file mode 100644 index 0000000..02e9106 --- /dev/null +++ b/src/Managing.Application/Accounts/AccountService.cs @@ -0,0 +1,172 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Users; +using Microsoft.Extensions.Logging; + +namespace Managing.Application.Accounts; +public class AccountService : IAccountService +{ + private readonly IAccountRepository _accountRepository; + private readonly IExchangeService _exchangeService; + private readonly IEvmManager _evmManager; + private readonly ICacheService _cacheService; + private readonly ILogger _logger; + + public AccountService( + IAccountRepository accountRepository, + ILogger logger, + IExchangeService exchangeService, + IEvmManager evmManager, + ICacheService cacheService) + { + _accountRepository = accountRepository; + _logger = logger; + _exchangeService = exchangeService; + _evmManager = evmManager; + _cacheService = cacheService; + } + + public async Task CreateAccount(User user, Account request) + { + var account = await _accountRepository.GetAccountByNameAsync(request.Name); + + if (account != null) + { + throw new Exception($"Account {request.Name} alreary exist"); + } + else + { + request.User = user; + + if (request.Exchange == Enums.TradingExchanges.Evm + && request.Type == Enums.AccountType.Trader) + { + var keys = _evmManager.GenerateAddress(); + request.Key = keys.Key; + request.Secret = keys.Secret; + } + else + { + request.Key = request.Key; + request.Secret = request.Secret; + } + + await _accountRepository.InsertAccountAsync(request); + } + + return request; + } + + public bool DeleteAccount(User user, string name) + { + try + { + _accountRepository.DeleteAccountByName(name); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + + public async Task GetAccount(string name, bool hideSecrets, bool getBalance) + { + var account = await _accountRepository.GetAccountByNameAsync(name); + ManageProperties(hideSecrets, getBalance, account); + + return account; + } + + public async Task GetAccountByKey(string key, bool hideSecrets, bool getBalance) + { + var account = await _accountRepository.GetAccountByKeyAsync(key); + ManageProperties(hideSecrets, getBalance, account); + + return account; + } + + public async Task GetAccountByUser(User user, string name, bool hideSecrets, bool getBalance) + { + var account = await _accountRepository.GetAccountByNameAsync(name); + ManageProperties(hideSecrets, getBalance, account); + + return account; + } + + public IEnumerable GetAccounts(bool hideSecrets, bool getBalance) + { + var result = _accountRepository.GetAccounts(); + var accounts = new List(); + + foreach (var account in result) + { + ManageProperties(hideSecrets, getBalance, account); + accounts.Add(account); + } + + return accounts; + } + + public IEnumerable GetAccountsByUser(User user, bool hideSecrets = true) + { + var cacheKey = $"user-account-{user.Name}"; + + return _cacheService.GetOrSave(cacheKey, () => + { + return GetAccounts(user, hideSecrets, false); + }, TimeSpan.FromMinutes(5)); + } + + private IEnumerable GetAccounts(User user, bool hideSecrets, bool getBalance) + { + var result = _accountRepository.GetAccounts(); + var accounts = new List(); + + foreach (var account in result.Where(a => a.User.Name == user.Name)) + { + ManageProperties(hideSecrets, getBalance, account); + accounts.Add(account); + } + + return accounts; + } + + public IEnumerable GetAccountsBalancesByUser(User user, bool hideSecrets) + { + var cacheKey = $"user-account-balance-{user.Name}"; + var accounts = _cacheService.GetOrSave(cacheKey, () => + { + return GetAccounts(user, true, true); + }, TimeSpan.FromHours(3)); + + return accounts; + } + + private void ManageProperties(bool hideSecrets, bool getBalance, Account account) + { + if (account != null) + { + if (getBalance) + { + try + { + account.Balances = _exchangeService.GetBalances(account).Result; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Cannot get account {account.Name} balance"); + } + } + + if (hideSecrets) + { + account.Secret = ""; + } + } + } +} \ No newline at end of file diff --git a/src/Managing.Application/Backtesting/Backtester.cs b/src/Managing.Application/Backtesting/Backtester.cs new file mode 100644 index 0000000..57bb463 --- /dev/null +++ b/src/Managing.Application/Backtesting/Backtester.cs @@ -0,0 +1,192 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Core; +using Managing.Domain.Accounts; +using Managing.Domain.Backtests; +using Managing.Domain.Candles; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Scenarios; +using Managing.Domain.Shared.Helpers; +using Managing.Domain.Workflows; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Application.Backtesting +{ + public class Backtester : IBacktester + { + private readonly IBacktestRepository _backtestRepository; + private readonly ILogger _logger; + private readonly IExchangeService _exchangeService; + private readonly IBotFactory _botFactory; + + public Backtester( + IExchangeService exchangeService, + IBotFactory botFactory, + IBacktestRepository backtestRepository, + ILogger logger) + { + _exchangeService = exchangeService; + _botFactory = botFactory; + _backtestRepository = backtestRepository; + _logger = logger; + } + + public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false) + { + var simplebot = _botFactory.CreateSimpleBot("scenario", workflow); + Backtest result = null; + if (save) + { + _backtestRepository.InsertBacktest(result); + } + return result; + } + + public Backtest RunScalpingBotBacktest( + Account account, + MoneyManagement moneyManagement, + Ticker ticker, + Scenario scenario, + Timeframe timeframe, + double days, + decimal balance, + bool isForWatchingOnly = false, + bool save = false) + { + var scalpingBot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, isForWatchingOnly); + scalpingBot.LoadStrategies(ScenarioHelpers.GetStrategiesFromScenario(scenario)); + var candles = GetCandles(account, ticker, timeframe, days); + var result = GetBacktestingResult(ticker, scenario, timeframe, scalpingBot, candles, balance, account, moneyManagement); + if (save) + { + _backtestRepository.InsertBacktest(result); + } + return result; + } + + private List GetCandles(Account account, Ticker ticker, Timeframe timeframe, double days) + { + var candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker, DateTime.Now.AddDays(Convert.ToDouble(days)), timeframe).Result; + + if (candles == null || candles.Count == 0) + throw new Exception($"No candles for {ticker} on {account.Exchange}"); + + return candles; + } + + public Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker, Scenario scenario, Timeframe timeframe, + double days, decimal balance, bool isForWatchingOnly = false, bool save = false) + { + var flippingBot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, false); + var strategy = ScenarioHelpers.GetStrategiesFromScenario(scenario); + flippingBot.LoadStrategies(ScenarioHelpers.GetStrategiesFromScenario(scenario)); + var candles = GetCandles(account, ticker, timeframe, days); + var result = GetBacktestingResult(ticker, scenario, timeframe, flippingBot, candles, balance, account, moneyManagement); + if (save) + { + _backtestRepository.InsertBacktest(result); + } + return result; + } + + public Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List candles, decimal balance) + { + var ticker = MiscExtensions.ParseEnum(candles.FirstOrDefault().Ticker); + var bot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, false); + bot.LoadStrategies(ScenarioHelpers.GetStrategiesFromScenario(scenario)); + var result = GetBacktestingResult(ticker, scenario, timeframe, bot, candles, balance, account, moneyManagement); + return result; + } + + public Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario, Timeframe timeframe, List candles, decimal balance) + { + var ticker = MiscExtensions.ParseEnum(candles.FirstOrDefault().Ticker); + var bot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario", timeframe, false); + var result = GetBacktestingResult(ticker, scenario, timeframe, bot, candles, balance, account, moneyManagement); + return result; + } + + private static Backtest GetBacktestingResult( + Ticker ticker, + Scenario scenario, + Timeframe timeframe, + ITradingBot bot, + List candles, + decimal balance, + Account account, + MoneyManagement moneyManagement) + { + if (candles == null || candles.Count == 0) + { + throw new Exception("No candle to backtest"); + } + + var hodlBalances = new Dictionary(); + bot.WalletBalances.Add(candles.FirstOrDefault().Date, balance); + foreach (var candle in candles) + { + hodlBalances.Add(candle.Date, candle.Close); + bot.Candles.Add(candle); + bot.Run(); + } + + var finalPnl = bot.GetProfitAndLoss(); + var winRate = bot.GetWinRate(); + var optimizedMoneyManagement = TradingBox.GetBestMoneyManagement(candles, bot.Positions, moneyManagement); + var stats = TradingHelpers.GetStatistics(bot.WalletBalances); + var growthPercentage = TradingHelpers.GetGrowthFromInitalBalance(balance, finalPnl); + var hodlPercentage = TradingHelpers.GetHodlPercentage(candles[0], candles.Last()); + var result = new Backtest(ticker, scenario.Name, bot.Positions, bot.Signals.ToList(), timeframe, candles, bot.BotType, account.Name) + { + FinalPnl = finalPnl, + WinRate = winRate, + GrowthPercentage = growthPercentage, + HodlPercentage = hodlPercentage, + Fees = bot.GetTotalFees(), + WalletBalances = bot.WalletBalances.ToList(), + Statistics = stats, + OptimizedMoneyManagement = optimizedMoneyManagement, + MoneyManagement = moneyManagement + }; + + return result; + } + + + + public IEnumerable GetBacktests() + { + return _backtestRepository.GetBacktests(); + } + + public bool DeleteBacktest(string id) + { + try + { + _backtestRepository.DeleteBacktestById(id); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + + public bool DeleteBacktests() + { + try + { + _backtestRepository.DeleteAllBacktests(); + //_backtestRepository.DropCollection(); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + } +} diff --git a/src/Managing.Application/Bots/Base/BotFactory.cs b/src/Managing.Application/Bots/Base/BotFactory.cs new file mode 100644 index 0000000..539f3f5 --- /dev/null +++ b/src/Managing.Application/Bots/Base/BotFactory.cs @@ -0,0 +1,111 @@ +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Workflows; +using Managing.Domain.Bots; + +namespace Managing.Application.Bots.Base +{ + public class BotFactory : IBotFactory + { + private readonly IMoneyManagementService _moneyManagementService; + private readonly IExchangeService _exchangeService; + private readonly IMessengerService _messengerService; + private readonly IAccountService _accountService; + private readonly ILogger _tradingBotLogger; + private readonly ITradingService _tradingService; + + public BotFactory( + IExchangeService exchangeService, + ILogger tradingBotLogger, + IMoneyManagementService moneyManagementService, + IMessengerService messengerService, + IAccountService accountService, + ITradingService tradingService) + { + _tradingBotLogger = tradingBotLogger; + _exchangeService = exchangeService; + _moneyManagementService = moneyManagementService; + _messengerService = messengerService; + _accountService = accountService; + _tradingService = tradingService; + } + + IBot IBotFactory.CreateSimpleBot(string botName, Workflow workflow) + { + return new SimpleBot(botName, _tradingBotLogger, workflow); + } + + ITradingBot IBotFactory.CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly) + { + return new ScalpingBot( + accountName, + moneyManagement, + name, + scenario, + _exchangeService, + ticker, + _tradingService, + _tradingBotLogger, + interval, + _accountService, + _messengerService, + isForWatchingOnly: isForWatchingOnly); + } + + ITradingBot IBotFactory.CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly) + { + return new ScalpingBot( + accountName, + moneyManagement, + "BacktestBot", + scenario, + _exchangeService, + ticker, + _tradingService, + _tradingBotLogger, + interval, + _accountService, + _messengerService, + true, + isForWatchingOnly); + } + + public ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly) + { + return new FlippingBot( + accountName, + moneyManagement, + name, + scenario, + _exchangeService, + ticker, + _tradingService, + _tradingBotLogger, + interval, + _accountService, + _messengerService, + isForWatchingOnly: isForWatchingOnly); + } + + public ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly) + { + return new FlippingBot( + accountName, + moneyManagement, + "BacktestBot", + scenario, + _exchangeService, + ticker, + _tradingService, + _tradingBotLogger, + interval, + _accountService, + _messengerService, + true, + isForWatchingOnly); + } + } +} diff --git a/src/Managing.Application/Bots/FlippingBot.cs b/src/Managing.Application/Bots/FlippingBot.cs new file mode 100644 index 0000000..058a910 --- /dev/null +++ b/src/Managing.Application/Bots/FlippingBot.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; +using Managing.Application.Abstractions.Services; +using Managing.Domain.MoneyManagements; + +namespace Managing.Application.Bots +{ + public class FlippingBot : TradingBot + { + public FlippingBot(string accountName, + MoneyManagement moneyManagement, + string name, + string scenario, + IExchangeService exchangeService, + Ticker ticker, + ITradingService tradingService, + ILogger logger, + Timeframe timeframe, + IAccountService accountService, + IMessengerService messengerService, + bool isForBacktest = false, + bool isForWatchingOnly = false) + : base(accountName, + moneyManagement, + name, + ticker, + scenario, + exchangeService, + logger, + tradingService, + timeframe, + accountService, + messengerService, + isForBacktest, + isForWatchingOnly, + flipPosition: true) + { + BotType = BotType.FlippingBot; + Start(); + } + + public sealed override void Start() + { + Logger.LogInformation($"{Name} - Bot Started"); + base.Start(); + Logger.LogInformation($"Starting {Name} bot - Status : {Status}"); + } + } +} diff --git a/src/Managing.Application/Bots/ScalpingBot.cs b/src/Managing.Application/Bots/ScalpingBot.cs new file mode 100644 index 0000000..f770c15 --- /dev/null +++ b/src/Managing.Application/Bots/ScalpingBot.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; +using Managing.Application.Abstractions.Services; +using Managing.Domain.MoneyManagements; + +namespace Managing.Application.Bots +{ + public class ScalpingBot : TradingBot + { + public ScalpingBot(string accountName, + MoneyManagement moneyManagement, + string name, + string scenario, + IExchangeService exchangeService, + Ticker ticker, + ITradingService tradingService, + ILogger logger, + Timeframe timeframe, + IAccountService accountService, + IMessengerService messengerService, + bool isForBacktest = false, + bool isForWatchingOnly = false) + : base(accountName, + moneyManagement, + name, + ticker, + scenario, + exchangeService, + logger, + tradingService, + timeframe, + accountService, + messengerService, + isForBacktest, + isForWatchingOnly) + { + BotType = BotType.ScalpingBot; + Start(); + } + + public sealed override void Start() + { + Logger.LogInformation($"{Name} - Bot Started"); + base.Start(); + Logger.LogInformation($"Starting {Name} bot - Status : {Status}"); + } + } +} diff --git a/src/Managing.Application/Bots/SimpleBot.cs b/src/Managing.Application/Bots/SimpleBot.cs new file mode 100644 index 0000000..ee8feb8 --- /dev/null +++ b/src/Managing.Application/Bots/SimpleBot.cs @@ -0,0 +1,39 @@ +using Managing.Domain.Bots; +using Managing.Domain.Workflows; +using Microsoft.Extensions.Logging; + +namespace Managing.Application.Bots +{ + public class SimpleBot : Bot + { + public readonly ILogger Logger; + private readonly Workflow _workflow; + + public SimpleBot(string name, ILogger logger, Workflow workflow) : base(name) + { + Logger = logger; + _workflow = workflow; + Interval = 100; + Start(); + } + + public override void Start() + { + Task.Run(() => InitWorker(Run)); + + base.Start(); + } + + public async Task Run() + { + await Task.Run( + async () => + { + Logger.LogInformation(Identifier); + Logger.LogInformation(DateTime.Now.ToString()); + await _workflow.Execute(); + Logger.LogInformation("__________________________________________________"); + }); + } + } +} diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs new file mode 100644 index 0000000..1dde4ab --- /dev/null +++ b/src/Managing.Application/Bots/TradingBot.cs @@ -0,0 +1,651 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Application.Trading; +using Managing.Application.Trading.Commands; +using Managing.Domain.Accounts; +using Managing.Domain.Bots; +using Managing.Domain.Candles; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Scenarios; +using Managing.Domain.Shared.Helpers; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Application.Bots; + +public class TradingBot : Bot, ITradingBot +{ + public readonly ILogger Logger; + public readonly IExchangeService ExchangeService; + public readonly IMessengerService MessengerService; + public readonly IAccountService AccountService; + private readonly ITradingService TradingService; + + public Account Account { get; set; } + public HashSet Strategies { get; set; } + public HashSet Candles { get; set; } + public HashSet Signals { get; set; } + public List Positions { get; set; } + public Ticker Ticker { get; set; } + public string Scenario { get; set; } + public string AccountName { get; set; } + public MoneyManagement MoneyManagement { get; set; } + public Timeframe Timeframe { get; set; } + public bool IsForBacktest { get; set; } + public DateTime PreloadSince { get; set; } + public bool IsForWatchingOnly { get; set; } + public bool FlipPosition { get; set; } + public int PreloadedCandlesCount { get; set; } + public BotType BotType { get; set; } + public decimal Fee { get; set; } + public Dictionary WalletBalances { get; set; } + + public TradingBot( + string accountName, + MoneyManagement moneyManagement, + string name, + Ticker ticker, + string scenario, + IExchangeService exchangeService, + ILogger logger, + ITradingService tradingService, + Timeframe timeframe, + IAccountService accountService, + IMessengerService messengerService, + bool isForBacktest = false, + bool isForWatchingOnly = false, + bool flipPosition = false) + : base(name) + { + ExchangeService = exchangeService; + AccountService = accountService; + MessengerService = messengerService; + TradingService = tradingService; + + IsForWatchingOnly = isForWatchingOnly; + FlipPosition = flipPosition; + AccountName = accountName; + MoneyManagement = moneyManagement; + Ticker = ticker; + Scenario = scenario; + Timeframe = timeframe; + IsForBacktest = isForBacktest; + Logger = logger; + + Strategies = new HashSet(); + Signals = new HashSet(); + Candles = new HashSet(); + Positions = new List(); + WalletBalances = new Dictionary(); + + if (!isForBacktest) + { + Interval = CandleExtensions.GetIntervalFromTimeframe(timeframe); + PreloadSince = CandleExtensions.GetBotPreloadSinceFromTimeframe(timeframe); + } + } + + public override async void Start() + { + base.Start(); + await LoadAccount(); + + if (!IsForBacktest) + { + LoadScenario(); + await PreloadCandles(); + await CancelAllOrders(); + await MessengerService.SendMessage($"Hi everyone, I'm going to run {Name}. \nI will send a message here everytime a signal is triggered by the {string.Join(",", Strategies.Select(s => s.Name))} strategies."); + await InitWorker(Run); + } + + Fee = TradingService.GetFee(Account, IsForBacktest); + } + + private async Task LoadAccount() + { + var account = await AccountService.GetAccount(AccountName, false, true); + if (account == null) + { + Logger.LogWarning($"No account found for this {AccountName}"); + Stop(); + } + else + { + Account = account; + } + } + + public void LoadScenario() + { + var scenario = TradingService.GetScenarioByName(Scenario); + if (scenario == null) + { + Logger.LogWarning("No scenario found for this scenario name"); + Stop(); + } + else + { + LoadStrategies(ScenarioHelpers.GetStrategiesFromScenario(scenario)); + } + } + + public void LoadStrategies(IEnumerable strategies) + { + foreach (var strategy in strategies) + { + Strategies.Add(strategy); + } + } + + public async Task Run() + { + Logger.LogInformation($"____________________{Name}____________________"); + Logger.LogInformation($"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Bot : {Name} - Type {BotType} - Ticker : {Ticker}"); + + var previousCandleCount = Candles.Count; + + if (!IsForBacktest) + await UpdateCandles(); + + if (Candles.Count > previousCandleCount || IsForBacktest) + await UpdateSignals(Candles); + else + Logger.LogInformation($"No need to update signals for {Ticker}"); + + if (!IsForWatchingOnly) + await ManagePositions(); + + await UpdateWalletBalances(); + Logger.LogInformation($"Candles : {Candles.Count}"); + Logger.LogInformation($"Signals : {Signals.Count}"); + Logger.LogInformation($"ExecutionCount : {ExecutionCount}"); + Logger.LogInformation($"Positions : {Positions.Count}"); + Logger.LogInformation("__________________________________________________"); + } + + private async Task PreloadCandles() + { + var candles = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, PreloadSince, Timeframe); + + foreach (var candle in candles.Where(c => c.Date < DateTime.Now.ToUniversalTime())) + { + if (!Candles.Any(c => c.Date == candle.Date)) + { + Candles.Add(candle); + await UpdateSignals(Candles); + } + } + + PreloadedCandlesCount = Candles.Count(); + } + + private async Task UpdateSignals(HashSet candles) + { + var signal = TradingBox.GetSignal(candles, Strategies, Signals); + + if (signal == null) return; + + await AddSignal(signal); + } + + + private async Task AddSignal(Signal signal) + { + Signals.Add(signal); + + if (!IsForBacktest) + TradingService.InsertSignal(signal); + + if (IsForWatchingOnly || (ExecutionCount < 1 && !IsForBacktest)) + signal.Status = SignalStatus.Expired; + + var signalText = $"{Scenario} trigger a signal. Signal told you " + + $"to {signal.Direction} {Ticker} on {Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}"; + + Logger.LogInformation(signalText); + + if (IsForWatchingOnly && !IsForBacktest && ExecutionCount > 0) + { + await MessengerService.SendSignal(signalText, Account.Exchange, Ticker, signal.Direction, Timeframe); + } + } + + protected async Task UpdateCandles() + { + if (Candles.Count == 0 || ExecutionCount == 0) + return; + + var lastCandle = Candles.Last(); + var newCandle = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, lastCandle.Date, Timeframe); + + foreach (var candle in newCandle.Where(c => c.Date < DateTime.Now.ToUniversalTime())) + { + Candles.Add(candle); + } + } + + private async Task ManagePositions() + { + if (!IsForBacktest && ExecutionCount < 1) + return; + + // Update position + foreach (var signal in Signals.Where(s => s.Status == SignalStatus.PositionOpen)) + { + var positionForSignal = Positions.FirstOrDefault(p => p.SignalIdentifier == signal.Identifier); + await UpdatePosition(signal, positionForSignal); + } + + // Open position for signal waiting for a position open + foreach (var signal in Signals.Where(s => s.Status == SignalStatus.WaitingForPosition)) + { + Task.Run(() => OpenPosition(signal)).GetAwaiter().GetResult(); + } + } + + private async Task UpdateWalletBalances() + { + if (WalletBalances.Count == 0) + { + WalletBalances.Add(Candles.LastOrDefault().Date, await ExchangeService.GetBalance(Account, IsForBacktest)); + } + else if (!WalletBalances.Any(w => w.Key == Candles.LastOrDefault().Date)) + { + var walletBalance = WalletBalances.FirstOrDefault().Value + GetProfitAndLoss(); + WalletBalances.Add(Candles.LastOrDefault().Date, walletBalance); + } + } + + private async Task UpdatePosition(Signal signal, Position positionForSignal) + { + try + { + Logger.LogInformation($"Updating position {positionForSignal.SignalIdentifier}"); + + var position = IsForBacktest ? positionForSignal : TradingService.GetPositionByIdentifier(positionForSignal.Identifier); + + if (position.Status == (PositionStatus.Finished | PositionStatus.Flipped)) + { + await HandleClosedPosition(positionForSignal); + return; + } + else if (position.Status == PositionStatus.Filled) + { + // For backtesting or force close if not executed on exchange : + // check if position is still open + // Check status, if still open update the status of the position + var lastCandle = IsForBacktest ? Candles.Last() : ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow); + + if (positionForSignal.OriginDirection == TradeDirection.Long) + { + if (positionForSignal.StopLoss.Price >= lastCandle.Low) + { + await LogInformation($"Closing position - SL {positionForSignal.StopLoss.Price} >= Price {lastCandle.Low}"); + await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, positionForSignal.StopLoss.Price, true); + positionForSignal.StopLoss.SetStatus(TradeStatus.Filled); + } + else if (positionForSignal.TakeProfit1.Price <= lastCandle.High + && positionForSignal.TakeProfit1.Status != TradeStatus.Filled) + { + await LogInformation($"Closing position - TP1 {positionForSignal.TakeProfit1.Price} <= Price {lastCandle.High}"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null); + positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled); + } + else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High) + { + await LogInformation($"Closing position - TP2 {positionForSignal.TakeProfit2.Price} <= Price {lastCandle.High}"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, positionForSignal.TakeProfit2.Price, true); + positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled); + } + else + { + Logger.LogInformation($"Position {signal.Identifier} don't need to be update. Position still opened"); + } + } + + if (positionForSignal.OriginDirection == TradeDirection.Short) + { + if (positionForSignal.StopLoss.Price <= lastCandle.High) + { + await LogInformation($"Closing position - SL {positionForSignal.StopLoss.Price} <= Price {lastCandle.High}"); + await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, positionForSignal.StopLoss.Price, true); + positionForSignal.StopLoss.SetStatus(TradeStatus.Filled); + } + else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low + && positionForSignal.TakeProfit1.Status != TradeStatus.Filled) + { + await LogInformation($"Closing position - TP1 {positionForSignal.TakeProfit1.Price} >= Price {lastCandle.Low}"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null); + positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled); + } + else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low) + { + await LogInformation($"Closing position - TP2 {positionForSignal.TakeProfit2.Price} >= Price {lastCandle.Low}"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, positionForSignal.TakeProfit2.Price, true); + positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled); + } + else + { + Logger.LogInformation($"Position {signal.Identifier} don't need to be update. Position still opened"); + } + } + } + else if (position.Status == (PositionStatus.Rejected | PositionStatus.Canceled)) + { + await LogWarning($"Open position trade is rejected for signal {signal.Identifier}"); + // if position is not open + // Re-open the trade for the signal only if signal still up + //if (signal.Status == SignalStatus.PositionOpen) + //{ + // Logger.LogInformation($"Try to re-open position"); + // OpenPosition(signal); + //} + } + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Message); + return; + } + } + + + + private async Task OpenPosition(Signal signal) + { + // Check if a position is already open + Logger.LogInformation($"Opening position for {signal.Identifier}"); + + var openedPosition = Positions.FirstOrDefault(p => p.Status == PositionStatus.Filled + && p.SignalIdentifier != signal.Identifier); + + var lastPrice = IsForBacktest ? Candles.Last().Close : ExchangeService.GetPrice(Account, Ticker, DateTime.UtcNow); + + // If position open + if (openedPosition != null) + { + var previousSignal = Signals.First(s => s.Identifier == openedPosition.SignalIdentifier); + + // Check if signal is the opposite side => flip the position + if (openedPosition.OriginDirection == signal.Direction) + { + // An operation is already open for the same direction + await LogInformation($"Signal {signal.Identifier} try to open a position but {previousSignal.Identifier} is already open for the same direction"); + SetSignalStatus(signal.Identifier, SignalStatus.Expired); + } + else + { + // An operation is already open for the opposite direction + // ==> Flip the position + if (FlipPosition) + { + await LogInformation("Try to flip the position because of an opposite direction signal"); + await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true); + await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped); + await OpenPosition(signal); + await LogInformation($"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$"); + } + else + { + await LogWarning($"A position is already open for signal {previousSignal.Identifier}. Position flipping is currently not enable, the position will not be flipped."); + SetSignalStatus(signal.Identifier, SignalStatus.Expired); + } + } + } + else + { + if (!CanOpenPosition(signal)) + { + await LogInformation("Tried to open position but last position was a loss. Wait for an opposition direction side or wait x candles to open a new position"); + SetSignalStatus(signal.Identifier, SignalStatus.Expired); + return; + } + + await LogInformation($"Open position - Date: {signal.Date:T} - SignalIdentifier : {signal.Identifier} - Strategie : {signal.StrategyType}"); + + try + { + var command = new OpenPositionRequest( + AccountName, + MoneyManagement, + signal.Direction, + Ticker, + PositionInitiator.Bot, + signal.Date, + IsForBacktest, + lastPrice, + balance: WalletBalances.LastOrDefault().Value, + fee: Fee); + + var position = await new OpenPositionCommandHandler(ExchangeService, AccountService, TradingService) + .Handle(command); + + if (position != null) + { + if (position.Open.Status != TradeStatus.Cancelled) + { + position.SignalIdentifier = signal.Identifier; + Positions.Add(position); + SetSignalStatus(signal.Identifier, SignalStatus.PositionOpen); + + if (!IsForBacktest) + { + await MessengerService.SendPosition(position); + } + + Logger.LogInformation($"Position requested"); + } + else + { + await SetPositionStatus(signal.Identifier, PositionStatus.Rejected); + SetSignalStatus(signal.Identifier, SignalStatus.Expired); + } + } + } + catch (Exception ex) + { + SetSignalStatus(signal.Identifier, SignalStatus.Expired); + await LogWarning($"Cannot open trade : {ex.Message}"); + } + } + } + + private bool CanOpenPosition(Signal signal) + { + if (Positions.Count == 0) + return true; + + var lastPosition = Positions.LastOrDefault(p => p.IsFinished() + && p.SignalIdentifier != signal.Identifier + && p.ProfitAndLoss.Realized < 0 + && p.OriginDirection == signal.Direction); + + if (lastPosition == null) + return true; + + var tenCandleAgo = Candles.TakeLast(10).First(); + var positionSignal = Signals.FirstOrDefault(s => s.Identifier == lastPosition.SignalIdentifier); + + return positionSignal.Date < tenCandleAgo.Date; + } + + private async Task CloseTrade(Signal signal, Position position, Trade tradeToClose, decimal lastPrice, bool tradeClosingPosition = false) + { + if (position.TakeProfit2 != null && position.TakeProfit1.Status == TradeStatus.Filled && tradeToClose.TradeType == TradeType.StopMarket) + { + // If trade is the 2nd Take profit + tradeToClose.Quantity = position.TakeProfit2.Quantity; + } + + await LogInformation($"Trying to close trade {Ticker} at {lastPrice} - Type : {tradeToClose.TradeType} - Quantity : {tradeToClose.Quantity} " + + $"- Closing Position : {tradeClosingPosition}"); + + // Get status of position before closing it. The position might be already close by the exchange + if (!IsForBacktest && await ExchangeService.GetQuantityInPosition(Account, Ticker) == 0) + { + Logger.LogInformation($"Trade already close on exchange"); + await HandleClosedPosition(position); + } + else + { + var command = new ClosePositionCommand(position, lastPrice); + try + { + var closedPosition = await (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService)) + .Handle(command); + + if (closedPosition.Status == (PositionStatus.Finished | PositionStatus.Flipped)) + { + if (tradeClosingPosition) + { + await SetPositionStatus(signal.Identifier, PositionStatus.Finished); + } + + await HandleClosedPosition(closedPosition); + } + else + { + throw new Exception($"Wrong position status : {closedPosition.Status}"); + } + } + catch (Exception ex) + { + await LogWarning($"Position {signal.Identifier} not closed : {ex.Message}"); + + if (position.Status == (PositionStatus.Canceled | PositionStatus.Rejected)) + { + // Trade close on exchange => Should close trade manually + await SetPositionStatus(signal.Identifier, PositionStatus.Finished); + } + } + + } + } + + private async Task HandleClosedPosition(Position position) + { + if (Positions.Any(p => p.Identifier == position.Identifier)) + { + var previousPosition = Positions.First(p => p.Identifier == position.Identifier); + var positionIndex = Positions.IndexOf(previousPosition); + position.SignalIdentifier = previousPosition.SignalIdentifier; + Positions[positionIndex] = position; + SetSignalStatus(position.SignalIdentifier, SignalStatus.Expired); + Logger.LogInformation($"Position {position.SignalIdentifier} type correctly close. Pnl on position : {position.ProfitAndLoss.Realized}"); + } + else + { + await LogWarning("Weird things happen - Trying to update position status, but no position found"); + } + + if (!IsForBacktest) + { + await MessengerService.SendClosingPosition(position); + } + + await CancelAllOrders(); + } + + private async Task CancelAllOrders() + { + if (!IsForBacktest) + { + try + { + var closePendingOrderStatus = await ExchangeService.CancelOrder(Account, Ticker); + Logger.LogInformation($"Closing all {Ticker} orders status : {closePendingOrderStatus}"); + } + catch (Exception ex) + { + // Todo handle exception from evm + Logger.LogError(ex, "Error during cancelOrders"); + } + } + } + + private async Task SetPositionStatus(string signalIdentifier, PositionStatus positionStatus) + { + await LogInformation($"Position {signalIdentifier} is now {positionStatus}"); + Positions.First(p => p.SignalIdentifier == signalIdentifier).Status = positionStatus; + SetSignalStatus(signalIdentifier, SignalStatus.Expired); + } + + private void SetSignalStatus(string signalIdentifier, SignalStatus signalStatus) + { + if (Signals.Any(s => s.Identifier == signalIdentifier)) + { + Signals.First(s => s.Identifier == signalIdentifier).Status = signalStatus; + Logger.LogInformation($"Signal {signalIdentifier} is now {signalStatus}"); + } + } + + public int GetWinRate() + { + var succeededPositions = Positions.Where(p => p.IsFinished()).Count(p => p.ProfitAndLoss?.Realized > 0); + var total = Positions.Where(p => p.IsFinished()).Count(); + + if (total == 0) + return 0; + + return (succeededPositions * 100) / total; + } + + public decimal GetProfitAndLoss() + { + var pnl = Positions.Where(p => p.ProfitAndLoss != null).Sum(p => p.ProfitAndLoss.Realized); + return pnl - GetTotalFees(); + } + + /// + /// Calculates the total fees paid by the trading bot for each position. + /// + /// Returns the total fees paid as a decimal value. + public decimal GetTotalFees() + { + decimal fees = 0; + foreach (var position in Positions.Where(p => p.Open.Fee > 0)) + { + fees += position.Open.Fee; + fees += position.StopLoss.Status == TradeStatus.Filled ? position.StopLoss.Fee : 0; + fees += position.TakeProfit1.Status == TradeStatus.Filled ? position.TakeProfit1.Fee : 0; + + if (position.IsFinished() && + position.StopLoss.Status != TradeStatus.Filled && position.TakeProfit1.Status != TradeStatus.Filled) + fees += position.Open.Fee; + + if (position.TakeProfit2 != null) + fees += position.TakeProfit2.Fee; + } + + return fees; + } + + public async Task ToggleIsForWatchOnly() + { + IsForWatchingOnly = (!IsForWatchingOnly); + await LogInformation($"Watch only toggle for bot : {Name} - Watch only : {IsForWatchingOnly}"); + } + + private async Task LogInformation(string message) + { + Logger.LogInformation(message); + await SendTradeMessage(message); + } + + private async Task LogWarning(string message) + { + Logger.LogWarning(message); + await SendTradeMessage(message, true); + } + + private async Task SendTradeMessage(string message, bool isBadBehavior = false) + { + if (!IsForBacktest) + { + await MessengerService.SendTradeMessage(message, isBadBehavior); + } + } +} diff --git a/src/Managing.Application/Hubs/BacktestHub.cs b/src/Managing.Application/Hubs/BacktestHub.cs new file mode 100644 index 0000000..0b29eb1 --- /dev/null +++ b/src/Managing.Application/Hubs/BacktestHub.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Managing.Application.Hubs; + +public class BacktestHub : Hub +{ + public async override Task OnConnectedAsync() + { + await base.OnConnectedAsync(); + await Clients.Caller.SendAsync("Message", $"Connected successfully on backtest hub. ConnectionId : {Context.ConnectionId}"); + } + + public async Task SubscribeBots() => + await Clients.All.SendAsync("BacktestsSubscription", "Successfully subscribed"); +} diff --git a/src/Managing.Application/Hubs/BotHub.cs b/src/Managing.Application/Hubs/BotHub.cs new file mode 100644 index 0000000..a85ad6b --- /dev/null +++ b/src/Managing.Application/Hubs/BotHub.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Managing.Application.Hubs; + +public class BotHub : Hub +{ + public async override Task OnConnectedAsync() + { + await base.OnConnectedAsync(); + await Clients.Caller.SendAsync("Message", "Connected successfully!"); + } + + public async Task SubscribeBots() => + await Clients.All.SendAsync("BotsSubscription", "Successfully subscribed"); + + public string GetConnectionId() => Context.ConnectionId; +} \ No newline at end of file diff --git a/src/Managing.Application/Hubs/CandleHub.cs b/src/Managing.Application/Hubs/CandleHub.cs new file mode 100644 index 0000000..83ffd3a --- /dev/null +++ b/src/Managing.Application/Hubs/CandleHub.cs @@ -0,0 +1,41 @@ +using Managing.Application.Abstractions.Services; +using Microsoft.AspNetCore.SignalR; + +namespace Managing.Application.Hubs; + +public class CandleHub : Hub +{ + private int ConnectionCount = 0; + private readonly IStreamService _streamService; + + public CandleHub(IStreamService streamService) + { + _streamService = streamService; + } + + public async override Task OnConnectedAsync() + { + ConnectionCount++; + + await Clients.Caller.SendAsync("Message", $"Connected successfully on candle hub. ConnectionId : {Context.ConnectionId}"); + + //await _streamService.SubscribeCandle(async (candle) => { + // await Clients.All.SendAsync("Candle", candle); + //}); + await _streamService.SubscribeCandle(); + await base.OnConnectedAsync(); + + } + + public override async Task OnDisconnectedAsync(Exception ex) + { + await Clients.Caller.SendAsync("Message", $"Shuting down candle hub. ConnectionId : {Context.ConnectionId}"); + + ConnectionCount--; + if(ConnectionCount == 0) + { + await _streamService.UnSubscribeCandle(); + } + await base.OnDisconnectedAsync(ex); + } +} diff --git a/src/Managing.Application/Hubs/PositionHub.cs b/src/Managing.Application/Hubs/PositionHub.cs new file mode 100644 index 0000000..c3ea49e --- /dev/null +++ b/src/Managing.Application/Hubs/PositionHub.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Managing.Application.Hubs; + +public class PositionHub : Hub +{ + public async override Task OnConnectedAsync() + { + await base.OnConnectedAsync(); + await Clients.Caller.SendAsync("Message", $"Connected successfully on position hub. ConnectionId : {Context.ConnectionId}"); + } +} diff --git a/src/Managing.Application/ManageBot/Commands/DeleteBotCommand.cs b/src/Managing.Application/ManageBot/Commands/DeleteBotCommand.cs new file mode 100644 index 0000000..60ecaf8 --- /dev/null +++ b/src/Managing.Application/ManageBot/Commands/DeleteBotCommand.cs @@ -0,0 +1,13 @@ +using MediatR; + +namespace Managing.Application.ManageBot.Commands; + +public class DeleteBotCommand : IRequest +{ + public string Name { get; } + + public DeleteBotCommand(string name) + { + Name = name; + } +} diff --git a/src/Managing.Application/ManageBot/Commands/GetActiveBotsCommand.cs b/src/Managing.Application/ManageBot/Commands/GetActiveBotsCommand.cs new file mode 100644 index 0000000..ce89a51 --- /dev/null +++ b/src/Managing.Application/ManageBot/Commands/GetActiveBotsCommand.cs @@ -0,0 +1,12 @@ +using Managing.Application.Abstractions; +using MediatR; + +namespace Managing.Application.ManageBot.Commands +{ + public class GetActiveBotsCommand : IRequest> + { + public GetActiveBotsCommand() + { + } + } +} diff --git a/src/Managing.Application/ManageBot/Commands/RestartBotCommand.cs b/src/Managing.Application/ManageBot/Commands/RestartBotCommand.cs new file mode 100644 index 0000000..40ee74a --- /dev/null +++ b/src/Managing.Application/ManageBot/Commands/RestartBotCommand.cs @@ -0,0 +1,14 @@ +using MediatR; + +namespace Managing.Application.ManageBot.Commands +{ + public class ToggleIsForWatchingCommand : IRequest + { + public string Name { get; } + + public ToggleIsForWatchingCommand(string name) + { + Name = name; + } + } +} diff --git a/src/Managing.Application/ManageBot/Commands/StartBotCommand.cs b/src/Managing.Application/ManageBot/Commands/StartBotCommand.cs new file mode 100644 index 0000000..90c7833 --- /dev/null +++ b/src/Managing.Application/ManageBot/Commands/StartBotCommand.cs @@ -0,0 +1,36 @@ +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.ManageBot.Commands +{ + public class StartBotCommand : IRequest + { + public string Name { get; } + public BotType BotType { get; } + public Ticker Ticker { get; internal set; } + public Timeframe Timeframe { get; internal set; } + public bool IsForWatchingOnly { get; internal set; } + public string Scenario { get; internal set; } + public string AccountName { get; internal set; } + public string MoneyManagementName { get; internal set; } + + public StartBotCommand(BotType botType, + string name, + Ticker ticker, + string scenario, + Timeframe timeframe, + string accountName, + string moneyManagementName, + bool isForWatchingOnly = false) + { + BotType = botType; + Name = name; + Scenario = scenario; + Ticker = ticker; + Timeframe = timeframe; + IsForWatchingOnly = isForWatchingOnly; + AccountName = accountName; + MoneyManagementName = moneyManagementName; + } + } +} diff --git a/src/Managing.Application/ManageBot/Commands/StopBotCommand.cs b/src/Managing.Application/ManageBot/Commands/StopBotCommand.cs new file mode 100644 index 0000000..d0132b1 --- /dev/null +++ b/src/Managing.Application/ManageBot/Commands/StopBotCommand.cs @@ -0,0 +1,17 @@ +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.ManageBot.Commands +{ + public class StopBotCommand : IRequest + { + public string Name { get; } + public BotType BotType { get; } + + public StopBotCommand(BotType botType, string name) + { + BotType = botType; + Name = name; + } + } +} diff --git a/src/Managing.Application/ManageBot/Commands/ToggleIsForWatchingCommand.cs b/src/Managing.Application/ManageBot/Commands/ToggleIsForWatchingCommand.cs new file mode 100644 index 0000000..d92a34a --- /dev/null +++ b/src/Managing.Application/ManageBot/Commands/ToggleIsForWatchingCommand.cs @@ -0,0 +1,17 @@ +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.ManageBot.Commands +{ + public class RestartBotCommand : IRequest + { + public string Name { get; } + public BotType BotType { get; } + + public RestartBotCommand(BotType botType, string name) + { + BotType = botType; + Name = name; + } + } +} diff --git a/src/Managing.Application/ManageBot/DeleteBotCommandHandler.cs b/src/Managing.Application/ManageBot/DeleteBotCommandHandler.cs new file mode 100644 index 0000000..36c4757 --- /dev/null +++ b/src/Managing.Application/ManageBot/DeleteBotCommandHandler.cs @@ -0,0 +1,32 @@ +using Managing.Application.Abstractions; +using Managing.Application.ManageBot.Commands; +using MediatR; +using Microsoft.Extensions.Logging; + +namespace Managing.Application.ManageBot; + +public class DeleteBotCommandHandler : IRequestHandler +{ + private readonly ILogger _log; + private readonly ITaskCache _taskCache; + + public DeleteBotCommandHandler(ITaskCache taskCache, ILogger log) + { + _taskCache = taskCache; + _log = log; + } + + public Task Handle(DeleteBotCommand request, CancellationToken cancellationToken) + { + try + { + _taskCache.Invalidate(request.Name); + return Task.FromResult(true); + } + catch (Exception e) + { + _log.LogError(e.Message); + return Task.FromResult(false); + } + } +} diff --git a/src/Managing.Application/ManageBot/GetActiveBotsCommandHandler.cs b/src/Managing.Application/ManageBot/GetActiveBotsCommandHandler.cs new file mode 100644 index 0000000..4ee8e46 --- /dev/null +++ b/src/Managing.Application/ManageBot/GetActiveBotsCommandHandler.cs @@ -0,0 +1,30 @@ +using Managing.Application.Abstractions; +using Managing.Application.ManageBot.Commands; +using Managing.Core; +using MediatR; + +namespace Managing.Application.ManageBot +{ + public class GetActiveBotsCommandHandler : IRequestHandler> + { + private readonly ITaskCache taskCache; + + public GetActiveBotsCommandHandler(ITaskCache taskCache) + { + this.taskCache = taskCache; + } + + public Task> Handle(GetActiveBotsCommand request, CancellationToken cancellationToken) + { + var cachedTask = taskCache.GetCache>(); + var result = new List(); + + foreach (var item in cachedTask) + { + result.Add(item.Value.Result); + } + + return Task.FromResult(result); + } + } +} diff --git a/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs b/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs new file mode 100644 index 0000000..b8a1079 --- /dev/null +++ b/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs @@ -0,0 +1,39 @@ +using Managing.Application.Abstractions; +using Managing.Application.ManageBot.Commands; +using Managing.Domain.Bots; +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.ManageBot +{ + public class RestartBotCommandHandler : IRequestHandler + { + private readonly ITaskCache _taskCache; + + public RestartBotCommandHandler(ITaskCache taskCache) + { + _taskCache = taskCache; + } + + public Task Handle(RestartBotCommand request, CancellationToken cancellationToken) + { + switch (request.BotType) + { + case BotType.SimpleBot: + var simpleBot = _taskCache.Get(request.Name); + simpleBot.Restart(); + return Task.FromResult(simpleBot.GetStatus()); + case BotType.ScalpingBot: + var scalpingBot = _taskCache.Get(request.Name); + scalpingBot.Restart(); + return Task.FromResult(scalpingBot.GetStatus()); + case BotType.FlippingBot: + var flippingBot = _taskCache.Get(request.Name); + flippingBot.Restart(); + return Task.FromResult(flippingBot.GetStatus()); + default: + return Task.FromResult(BotStatus.Down.ToString()); + } + } + } +} diff --git a/src/Managing.Application/ManageBot/StartBotCommandHandler.cs b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs new file mode 100644 index 0000000..7828678 --- /dev/null +++ b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs @@ -0,0 +1,42 @@ +using Managing.Domain.Bots; +using MediatR; +using static Managing.Common.Enums; +using Managing.Application.Abstractions; +using Managing.Application.ManageBot.Commands; + +namespace Managing.Application.ManageBot +{ + public class StartBotCommandHandler : IRequestHandler + { + private readonly IBotFactory _botFactory; + private readonly ITaskCache _taskCache; + private readonly IMoneyManagementService _moneyManagementService; + + public StartBotCommandHandler(IBotFactory botFactory, ITaskCache taskCache, IMoneyManagementService moneyManagementService) + { + _botFactory = botFactory; + _taskCache = taskCache; + _moneyManagementService = moneyManagementService; + } + + public Task Handle(StartBotCommand request, CancellationToken cancellationToken) + { + BotStatus botStatus = BotStatus.Down; + var moneyManagement = _moneyManagementService.GetMoneyMangement(request.MoneyManagementName).Result; + switch (request.BotType) + { + case BotType.SimpleBot: + Func> simpleBot = () => Task.FromResult(_botFactory.CreateSimpleBot(request.Name, null)); + return Task.FromResult(_taskCache.AddOrGetExisting(request.Name, simpleBot).Result.GetStatus()); + case BotType.ScalpingBot: + Func> scalpingBot = () => Task.FromResult(_botFactory.CreateScalpingBot(request.AccountName, moneyManagement, request.Name, request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly)); + return Task.FromResult(_taskCache.AddOrGetExisting(request.Name, scalpingBot).Result.GetStatus()); + case BotType.FlippingBot: + Func> flippingBot = () => Task.FromResult(_botFactory.CreateFlippingBot(request.AccountName, moneyManagement, request.Name, request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly)); + return Task.FromResult(_taskCache.AddOrGetExisting(request.Name, flippingBot).Result.GetStatus()); + }; + + return Task.FromResult(botStatus.ToString()); + } + } +} diff --git a/src/Managing.Application/ManageBot/StopBotCommandHandler.cs b/src/Managing.Application/ManageBot/StopBotCommandHandler.cs new file mode 100644 index 0000000..5a0a701 --- /dev/null +++ b/src/Managing.Application/ManageBot/StopBotCommandHandler.cs @@ -0,0 +1,39 @@ +using Managing.Application.Abstractions; +using Managing.Application.ManageBot.Commands; +using Managing.Domain.Bots; +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.ManageBot +{ + public class StopBotCommandHandler : IRequestHandler + { + private readonly ITaskCache _taskCache; + + public StopBotCommandHandler(ITaskCache taskCache) + { + _taskCache = taskCache; + } + + public Task Handle(StopBotCommand request, CancellationToken cancellationToken) + { + switch (request.BotType) + { + case BotType.SimpleBot: + var simpleBot = _taskCache.Get(request.Name); + simpleBot.Stop(); + return Task.FromResult(simpleBot.GetStatus()); + case BotType.ScalpingBot: + var scalpingBot = _taskCache.Get(request.Name); + scalpingBot.Stop(); + return Task.FromResult(scalpingBot.GetStatus()); + case BotType.FlippingBot: + var flippingBot = _taskCache.Get(request.Name); + flippingBot.Stop(); + return Task.FromResult(flippingBot.GetStatus()); + default: + return Task.FromResult(BotStatus.Down.ToString()); + } + } + } +} diff --git a/src/Managing.Application/ManageBot/ToggleIsForWatchingCommandHandler.cs b/src/Managing.Application/ManageBot/ToggleIsForWatchingCommandHandler.cs new file mode 100644 index 0000000..d8ae52e --- /dev/null +++ b/src/Managing.Application/ManageBot/ToggleIsForWatchingCommandHandler.cs @@ -0,0 +1,23 @@ +using Managing.Application.Abstractions; +using Managing.Application.ManageBot.Commands; +using MediatR; + +namespace Managing.Application.ManageBot +{ + public class ToggleIsForWatchingCommandHandler : IRequestHandler + { + private readonly ITaskCache _taskCache; + + public ToggleIsForWatchingCommandHandler(ITaskCache taskCache) + { + _taskCache = taskCache; + } + + public async Task Handle(ToggleIsForWatchingCommand request, CancellationToken cancellationToken) + { + var bot = _taskCache.Get(request.Name); + await bot.ToggleIsForWatchOnly(); + return bot.GetStatus(); + } + } +} diff --git a/src/Managing.Application/Managing.Application.csproj b/src/Managing.Application/Managing.Application.csproj new file mode 100644 index 0000000..6996dc5 --- /dev/null +++ b/src/Managing.Application/Managing.Application.csproj @@ -0,0 +1,34 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Managing.Application/MoneyManagements/MoneyManagementService.cs b/src/Managing.Application/MoneyManagements/MoneyManagementService.cs new file mode 100644 index 0000000..0c2b63d --- /dev/null +++ b/src/Managing.Application/MoneyManagements/MoneyManagementService.cs @@ -0,0 +1,79 @@ +using Managing.Domain.MoneyManagements; +using Managing.Application.Abstractions; +using Microsoft.Extensions.Logging; +using Managing.Application.Abstractions.Repositories; + +namespace Managing.Application.MoneyManagements; + +public class MoneyManagementService : IMoneyManagementService +{ + private readonly ISettingsRepository _settingsRepository; + private readonly ILogger _logger; + + public MoneyManagementService( + ILogger logger, + ISettingsRepository settingsRepository) + { + _logger = logger; + _settingsRepository = settingsRepository; + } + + public async Task CreateOrUpdateMoneyManagement(MoneyManagement request) + { + var moneyManagement = await _settingsRepository.GetMoneyManagement(request.Name); + + if (moneyManagement == null) + { + await _settingsRepository.InsertMoneyManagement(request); + } + else + { + moneyManagement.StopLoss = request.StopLoss; + moneyManagement.TakeProfit = request.TakeProfit; + moneyManagement.BalanceAtRisk = request.BalanceAtRisk; + moneyManagement.Leverage = request.Leverage; + moneyManagement.Timeframe = request.Timeframe; + _settingsRepository.UpdateMoneyManagement(moneyManagement); + } + + return moneyManagement; + } + + public IEnumerable GetMoneyMangements() + { + return _settingsRepository.GetMoneyManagements(); + } + + public async Task GetMoneyMangement(string name) + { + return await _settingsRepository.GetMoneyManagement(name); + } + + public bool DeleteMoneyManagement(string name) + { + try + { + _settingsRepository.DeleteMoneyManagement(name); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + + public bool DeleteMoneyManagements() + { + try + { + _settingsRepository.DeleteMoneyManagements(); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } +} diff --git a/src/Managing.Application/Scenarios/ScenarioService.cs b/src/Managing.Application/Scenarios/ScenarioService.cs new file mode 100644 index 0000000..a00ed09 --- /dev/null +++ b/src/Managing.Application/Scenarios/ScenarioService.cs @@ -0,0 +1,144 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Domain.Scenarios; +using Managing.Domain.Strategies; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; +using static Managing.Common.Enums; + +namespace Managing.Application.Scenarios +{ + public class ScenarioService : IScenarioService + { + private readonly ILogger _logger; + private readonly ITradingService _tradingService; + + public ScenarioService(ILogger logger, ITradingService tradingService) + { + _logger = logger; + _tradingService = tradingService; + } + + public Scenario CreateScenario(string name, List strategies) + { + var scenario = new Scenario(name); + + foreach (var strategy in strategies) + { + scenario.AddStrategy(_tradingService.GetStrategyByName(strategy)); + } + + try + { + _tradingService.InsertScenario(scenario); + } + catch (MongoCommandException ex) + { + _logger.LogError(ex.Message); + throw new Exception("Cannot create scenario"); + } + + return scenario; + } + + public Strategy CreateStrategy( + StrategyType type, + 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) + { + var strategy = ScenarioHelpers.BuildStrategy( + type, + timeframe, + name, + period, + fastPeriods, + slowPeriods, + signalPeriods, + multiplier, + stochPeriods, + smoothPeriods, + cyclePeriods); + _tradingService.InsertStrategy(strategy); + return strategy; + } + + public IEnumerable GetScenarios() + { + return _tradingService.GetScenarios(); + } + + public Scenario GetScenario(string name) + { + return _tradingService.GetScenarioByName(name); + } + + public IEnumerable GetStrategies() + { + return _tradingService.GetStrategies(); + } + + public bool DeleteScenario(string name) + { + try + { + _tradingService.DeleteScenario(name); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + + public bool DeleteStrategy(string name) + { + try + { + _tradingService.DeleteStrategy(name); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + + public bool DeleteStrategies() + { + try + { + _tradingService.DeleteStrategies(); + } + catch (Exception) + { + return false; + } + + return true; + } + + public bool DeleteScenarios() + { + try + { + _tradingService.DeleteScenarios(); + } + catch (Exception) + { + return false; + } + + return true; + } + } +} diff --git a/src/Managing.Application/Shared/Behaviours/IUnhandledExceptionBehaviour.cs b/src/Managing.Application/Shared/Behaviours/IUnhandledExceptionBehaviour.cs new file mode 100644 index 0000000..a751874 --- /dev/null +++ b/src/Managing.Application/Shared/Behaviours/IUnhandledExceptionBehaviour.cs @@ -0,0 +1,9 @@ +using MediatR; + +namespace Managing.Application.Shared.Behaviours +{ + public interface IUnhandledExceptionBehaviour + { + Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next); + } +} \ No newline at end of file diff --git a/src/Managing.Application/Shared/Behaviours/UnhandledExceptionBehaviour.cs b/src/Managing.Application/Shared/Behaviours/UnhandledExceptionBehaviour.cs new file mode 100644 index 0000000..dc07235 --- /dev/null +++ b/src/Managing.Application/Shared/Behaviours/UnhandledExceptionBehaviour.cs @@ -0,0 +1,31 @@ +using MediatR; +using Microsoft.Extensions.Logging; + +namespace Managing.Application.Shared.Behaviours +{ + public class UnhandledExceptionBehaviour : IUnhandledExceptionBehaviour + { + private readonly ILogger logger; + + public UnhandledExceptionBehaviour(ILogger logger) + { + this.logger = logger; + } + + public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) + { + try + { + return await next(); + } + catch (Exception ex) + { + var requestName = typeof(TRequest).Name; + + logger.LogError(ex, $"Unhandled Exception for Request {requestName} {request}"); + + throw; + } + } + } +} diff --git a/src/Managing.Application/Shared/Behaviours/ValidationBehaviour.cs b/src/Managing.Application/Shared/Behaviours/ValidationBehaviour.cs new file mode 100644 index 0000000..7aa1e81 --- /dev/null +++ b/src/Managing.Application/Shared/Behaviours/ValidationBehaviour.cs @@ -0,0 +1,29 @@ +using FluentValidation; +using MediatR; + +namespace Managing.Application.Shared.Behaviours +{ + public class ValidationBehaviour : IPipelineBehavior + where TRequest : IRequest + { + private readonly IEnumerable> validators; + + public ValidationBehaviour(IEnumerable> validators) + { + this.validators = validators; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + if (validators.Any()) + { + var context = new ValidationContext(request); + var validationResults = await Task.WhenAll(validators.Select(v => v.ValidateAsync(context, cancellationToken))); + var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); + if (failures.Count != 0) throw new ValidationException(failures); + } + + return await next(); + } + } +} diff --git a/src/Managing.Application/Shared/MessengerService.cs b/src/Managing.Application/Shared/MessengerService.cs new file mode 100644 index 0000000..92ab997 --- /dev/null +++ b/src/Managing.Application/Shared/MessengerService.cs @@ -0,0 +1,66 @@ +using Managing.Application.Abstractions.Services; +using Managing.Common; +using Managing.Domain.Statistics; +using Managing.Domain.Trades; + +namespace Managing.Application.Shared; + +public class MessengerService : IMessengerService +{ + private readonly IDiscordService _discordService; + + public MessengerService(IDiscordService discordService) + { + _discordService = discordService; + } + + public async Task SendClosedPosition(string address, Trade oldTrade) + { + await _discordService.SendClosedPosition(address, oldTrade); + } + + public async Task SendClosingPosition(Position position) + { + await _discordService.SendClosingPosition(position); + } + + public async Task SendIncreasePosition(string address, Trade trade, string copyAccountName, Trade? oldTrade = null) + { + await _discordService.SendIncreasePosition(address, trade, copyAccountName, oldTrade); + } + + public async Task SendDecreasePosition(string address, Trade newTrade, decimal decreaseAmount) + { + await _discordService.SendDecreasePosition(address, newTrade, decreaseAmount); + } + + public async Task SendMessage(string message) + { + await _discordService.SendMessage(message); + } + + public async Task SendPosition(Position position) + { + await _discordService.SendPosition(position); + } + + public async Task SendSignal(string message, Enums.TradingExchanges exchange, Enums.Ticker ticker, Enums.TradeDirection direction, Enums.Timeframe timeframe) + { + await _discordService.SendSignal(message, exchange, ticker, direction, timeframe); + } + + public async Task SendTradeMessage(string message, bool isBadBehavior = false) + { + await _discordService.SendTradeMessage(message, isBadBehavior); + } + + public async Task SendBestTraders(List traders) + { + await _discordService.SendBestTraders(traders); + } + + public async Task SendBadTraders(List traders) + { + await _discordService.SendBadTraders(traders); + } +} diff --git a/src/Managing.Application/Shared/SettingsService.cs b/src/Managing.Application/Shared/SettingsService.cs new file mode 100644 index 0000000..76195c3 --- /dev/null +++ b/src/Managing.Application/Shared/SettingsService.cs @@ -0,0 +1,199 @@ +using Managing.Application.Abstractions; +using Managing.Domain.MoneyManagements; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Application.Shared; + +public class SettingsService : ISettingsService +{ + private readonly IMoneyManagementService _moneyManagementService; + private readonly IScenarioService _scenarioService; + private readonly IBacktester _backtester; + private readonly ILogger _logger; + + public SettingsService(IMoneyManagementService moneyManagementService, + IScenarioService scenarioService, + IBacktester backtester, + ILogger logger) + { + _moneyManagementService = moneyManagementService; + _scenarioService = scenarioService; + _backtester = backtester; + _logger = logger; + } + + public async Task ResetSettings() + { + if (!_backtester.DeleteBacktests()) + { + throw new Exception("Cannot delete all backtests"); + } + + if (!_scenarioService.DeleteScenarios()) + { + throw new Exception("Cannot delete scenarios"); + } + + if (!_scenarioService.DeleteStrategies()) + { + throw new Exception("Cannot delete all strategies"); + } + + if (!_moneyManagementService.DeleteMoneyManagements()) + { + throw new Exception("Cannot delete all money management settings"); + } + + if (!SetupSettings()) + { + throw new Exception("Cannot setup settings"); + } + + return await Task.FromResult(true); + } + + public bool SetupSettings() + { + try + { + SetupMoneyManagementsSeed(Timeframe.FiveMinutes); + SetupMoneyManagementsSeed(Timeframe.FifteenMinutes); + SetupMoneyManagementsSeed(Timeframe.OneHour); + SetupMoneyManagementsSeed(Timeframe.OneDay); + SetupScenariosSeed(Timeframe.FifteenMinutes); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + return true; + } + + private async void SetupMoneyManagementsSeed(Timeframe timeframe) + { + var moneyManagement = new MoneyManagement() + { + Timeframe = timeframe, + BalanceAtRisk = 0.05m, + Leverage = 1, + StopLoss = 0.021m, + TakeProfit = 0.042m, + Name = $"{timeframe} Money Management" + }; + + await _moneyManagementService.CreateOrUpdateMoneyManagement(moneyManagement); + } + + private void SetupScenariosSeed(Timeframe timeframe) + { + SetupMacd(timeframe); + SetupRsiDiv(timeframe); + SetupRsiDivConfirm(timeframe); + SetupSuperTrend(timeframe); + SetupChandelierExit(timeframe); + SetupStochRsiTrend(timeframe); + SetupStochSTCTrend(timeframe); + SetupEmaTrend(timeframe); + SetupEmaCross(timeframe); + } + + private void SetupStochSTCTrend(Timeframe timeframe) + { + var name = "STCTrend"; + var strategy = _scenarioService.CreateStrategy(StrategyType.Stc, + timeframe, + name, + fastPeriods: 23, + slowPeriods: 50, + cyclePeriods: 10); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + + private void SetupMacd(Timeframe timeframe) + { + var name = "MacdCross"; + var strategy = _scenarioService.CreateStrategy(StrategyType.MacdCross, + timeframe, + name, + fastPeriods: 12, + slowPeriods: 26, + signalPeriods: 9); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + + private void SetupRsiDiv(Timeframe timeframe) + { + var name = "RsiDiv6"; + var strategy = _scenarioService.CreateStrategy(StrategyType.RsiDivergence, + timeframe, + name, + period: 6); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + + private void SetupRsiDivConfirm(Timeframe timeframe) + { + var name = "RsiDivConfirm6"; + var strategy = _scenarioService.CreateStrategy(StrategyType.RsiDivergenceConfirm, + timeframe, + name, + period: 6); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + + private void SetupSuperTrend(Timeframe timeframe) + { + var name = "SuperTrend"; + var strategy = _scenarioService.CreateStrategy(StrategyType.SuperTrend, + timeframe, + name, + period: 10, + multiplier: 3); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + + private void SetupChandelierExit(Timeframe timeframe) + { + var name = "ChandelierExit"; + var strategy = _scenarioService.CreateStrategy(StrategyType.ChandelierExit, + timeframe, + name, + period: 22, + multiplier: 3); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + private void SetupStochRsiTrend(Timeframe timeframe) + { + var name = "StochRsiTrend"; + var strategy = _scenarioService.CreateStrategy(StrategyType.StochRsiTrend, + timeframe, + name, + period: 14, + stochPeriods: 14, + signalPeriods: 3, + smoothPeriods: 1); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + + private void SetupEmaTrend(Timeframe timeframe) + { + var name = "Ema200Trend"; + var strategy = _scenarioService.CreateStrategy(StrategyType.EmaTrend, + timeframe, + name, + period: 200); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } + + private void SetupEmaCross(Timeframe timeframe) + { + var name = "Ema200Cross"; + var strategy = _scenarioService.CreateStrategy(StrategyType.EmaCross, + timeframe, + name, + period: 200); + _scenarioService.CreateScenario(name, new List { strategy.Name }); + } +} diff --git a/src/Managing.Application/Shared/StreamService.cs b/src/Managing.Application/Shared/StreamService.cs new file mode 100644 index 0000000..c70f421 --- /dev/null +++ b/src/Managing.Application/Shared/StreamService.cs @@ -0,0 +1,30 @@ +using Managing.Application.Abstractions.Services; +using Managing.Application.Hubs; +using Microsoft.AspNetCore.SignalR; + +namespace Managing.Application.Shared; + +public class StreamService : IStreamService +{ + private readonly IExchangeStream _exchangeStream; + private readonly IHubContext _hubContext; + + + public StreamService(IExchangeStream exchangeStream, IHubContext hubContext) + { + _exchangeStream = exchangeStream; + _hubContext = hubContext; + } + + public async Task SubscribeCandle() + { + await _exchangeStream.StartBinanceWorker(Common.Enums.Ticker.BTC, async (candle) => { + await _hubContext.Clients.All.SendAsync(candle.Ticker, candle); + }); + } + + public async Task UnSubscribeCandle() + { + await _exchangeStream.StopBinanceWorker(); + } +} diff --git a/src/Managing.Application/Shared/TickerService.cs b/src/Managing.Application/Shared/TickerService.cs new file mode 100644 index 0000000..54e061e --- /dev/null +++ b/src/Managing.Application/Shared/TickerService.cs @@ -0,0 +1,21 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using static Managing.Common.Enums; + +namespace Managing.Application.Shared; + +public class TickerService : ITickerService +{ + private readonly IEvmManager _evmManager; + + public TickerService(IEvmManager evmManager) + { + _evmManager = evmManager; + } + + public async Task> GetAvailableTicker() + { + var tickers = await _evmManager.GetAvailableTicker(); + return tickers; + } +} diff --git a/src/Managing.Application/Trading/ClosePositionCommandHandler.cs b/src/Managing.Application/Trading/ClosePositionCommandHandler.cs new file mode 100644 index 0000000..477ab19 --- /dev/null +++ b/src/Managing.Application/Trading/ClosePositionCommandHandler.cs @@ -0,0 +1,55 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Application.Trading.Commands; +using Managing.Domain.Shared.Helpers; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Application.Trading; + +public class ClosePositionCommandHandler : ICommandHandler +{ + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + private readonly ITradingService _tradingService; + + public ClosePositionCommandHandler( + IExchangeService exchangeService, + IAccountService accountService, + ITradingService tradingService) + { + _exchangeService = exchangeService; + _accountService = accountService; + _tradingService = tradingService; + } + + public async Task Handle(ClosePositionCommand request) + { + // Get Trade + var account = await _accountService.GetAccount(request.Position.AccountName, false, false); + if (request.Position == null) + { + _ = _exchangeService.CancelOrder(account, request.Position.Ticker).Result; + return request.Position; + } + + var isForPaperTrading = request.Position.Initiator == PositionInitiator.PaperTrading; + + var lastPrice = request.Position.Initiator == PositionInitiator.PaperTrading ? + request.ExecutionPrice.GetValueOrDefault() : + _exchangeService.GetPrice(account, request.Position.Ticker, DateTime.UtcNow); + + // Close market + var closedPosition = _exchangeService.ClosePosition(account, request.Position, lastPrice, isForPaperTrading).Result; + var closeRequestedOrders = isForPaperTrading ? true : _exchangeService.CancelOrder(account, request.Position.Ticker).Result; + + if (closeRequestedOrders || closedPosition.Status == (TradeStatus.PendingOpen | TradeStatus.Filled)) + { + request.Position.Status = PositionStatus.Finished; + request.Position.ProfitAndLoss = TradingBox.GetProfitAndLoss(request.Position, closedPosition.Quantity, lastPrice); + _tradingService.UpdatePosition(request.Position); + } + + return request.Position; + } +} diff --git a/src/Managing.Application/Trading/Commands/ClosePositionCommand.cs b/src/Managing.Application/Trading/Commands/ClosePositionCommand.cs new file mode 100644 index 0000000..ee70784 --- /dev/null +++ b/src/Managing.Application/Trading/Commands/ClosePositionCommand.cs @@ -0,0 +1,17 @@ +using Managing.Domain.Trades; +using MediatR; + +namespace Managing.Application.Trading.Commands +{ + public class ClosePositionCommand : IRequest + { + public ClosePositionCommand(Position position, decimal? executionPrice = null) + { + Position = position; + ExecutionPrice = executionPrice; + } + + public Position Position { get; } + public decimal? ExecutionPrice { get; set; } + } +} diff --git a/src/Managing.Application/Trading/Commands/GetPositionsCommand.cs b/src/Managing.Application/Trading/Commands/GetPositionsCommand.cs new file mode 100644 index 0000000..4b8b6bd --- /dev/null +++ b/src/Managing.Application/Trading/Commands/GetPositionsCommand.cs @@ -0,0 +1,16 @@ +using Managing.Common; +using Managing.Domain.Trades; +using MediatR; + +namespace Managing.Application.Trading.Commands +{ + public class GetPositionsCommand : IRequest> + { + public GetPositionsCommand(Enums.PositionInitiator initiator) + { + Initiator = initiator; + } + + public Enums.PositionInitiator Initiator { get; internal set; } + } +} diff --git a/src/Managing.Application/Trading/Commands/GetTradeCommand.cs b/src/Managing.Application/Trading/Commands/GetTradeCommand.cs new file mode 100644 index 0000000..a5dea6d --- /dev/null +++ b/src/Managing.Application/Trading/Commands/GetTradeCommand.cs @@ -0,0 +1,20 @@ +using Managing.Domain.Trades; +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.Trading.Commands +{ + public class GetTradeCommand : IRequest + { + public GetTradeCommand(string accountName, string exchangeOrderId, Ticker ticker) + { + AccountName = accountName; + ExchangeOrderId = exchangeOrderId; + Ticker = ticker; + } + + public string AccountName { get; } + public string ExchangeOrderId { get; } + public Ticker Ticker { get; } + } +} diff --git a/src/Managing.Application/Trading/Commands/GetTradesCommand.cs b/src/Managing.Application/Trading/Commands/GetTradesCommand.cs new file mode 100644 index 0000000..050a35c --- /dev/null +++ b/src/Managing.Application/Trading/Commands/GetTradesCommand.cs @@ -0,0 +1,18 @@ +using Managing.Domain.Trades; +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.Trading.Commands +{ + public class GetTradesCommand : IRequest> + { + public GetTradesCommand(Ticker ticker, string accountName) + { + Ticker = ticker; + AccountName = accountName; + } + + public string AccountName { get; } + public Ticker Ticker { get; } + } +} diff --git a/src/Managing.Application/Trading/Commands/OpenPositionRequest.cs b/src/Managing.Application/Trading/Commands/OpenPositionRequest.cs new file mode 100644 index 0000000..a325214 --- /dev/null +++ b/src/Managing.Application/Trading/Commands/OpenPositionRequest.cs @@ -0,0 +1,48 @@ +using Managing.Domain.MoneyManagements; +using Managing.Domain.Trades; +using MediatR; +using static Managing.Common.Enums; + +namespace Managing.Application.Trading.Commands +{ + public class OpenPositionRequest : IRequest + { + public OpenPositionRequest( + string accountName, + MoneyManagement moneyManagement, + TradeDirection direction, + Ticker ticker, + PositionInitiator initiator, + DateTime date, + bool isForPaperTrading = false, + decimal? price = null, + decimal? balance = 1000, + decimal? fee = null, + bool? ignoreSLTP = false) + { + AccountName = accountName; + MoneyManagement = moneyManagement; + Direction = direction; + Ticker = ticker; + IsForPaperTrading = isForPaperTrading; + Price = price; + Date = date; + Balance = balance; + Initiator = initiator; + Fee = fee; + IgnoreSLTP = ignoreSLTP; + } + + public string AccountName { get; } + public MoneyManagement MoneyManagement { get; } + public TradeDirection Direction { get; } + public Ticker Ticker { get; } + public bool IsForPaperTrading { get; } + public decimal? Price { get; } + public decimal? Fee { get; } + public bool? IgnoreSLTP { get; } + public decimal? Balance { get; } + public DateTime Date { get; set; } + public PositionInitiator Initiator { get; internal set; } + } +} diff --git a/src/Managing.Application/Trading/GetPositionsCommandHandler.cs b/src/Managing.Application/Trading/GetPositionsCommandHandler.cs new file mode 100644 index 0000000..e8c9521 --- /dev/null +++ b/src/Managing.Application/Trading/GetPositionsCommandHandler.cs @@ -0,0 +1,23 @@ +using Managing.Application.Abstractions.Services; +using Managing.Application.Trading.Commands; +using Managing.Domain.Trades; +using MediatR; + +namespace Managing.Application.Trading +{ + public class GetPositionsCommandHandler : IRequestHandler> + { + private readonly ITradingService _tradingService; + + public GetPositionsCommandHandler(ITradingService tradingService) + { + _tradingService = tradingService; + } + + public Task> Handle(GetPositionsCommand request, CancellationToken cancellationToken) + { + var positions = _tradingService.GetPositions(request.Initiator); + return Task.FromResult(positions.ToList()); + } + } +} diff --git a/src/Managing.Application/Trading/GetTradeCommandHandler.cs b/src/Managing.Application/Trading/GetTradeCommandHandler.cs new file mode 100644 index 0000000..9f81d62 --- /dev/null +++ b/src/Managing.Application/Trading/GetTradeCommandHandler.cs @@ -0,0 +1,23 @@ +using Managing.Application.Abstractions.Services; +using Managing.Application.Trading.Commands; +using Managing.Domain.Trades; +using MediatR; + +namespace Managing.Application.Trading; + +public class GetTradeCommandHandler : IRequestHandler +{ + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + public GetTradeCommandHandler(IExchangeService exchangeService, IAccountService accountService) + { + _exchangeService = exchangeService; + _accountService = accountService; + } + + public Task Handle(GetTradeCommand request, CancellationToken cancellationToken) + { + var account = _accountService.GetAccount(request.AccountName, true, false).Result; + return _exchangeService.GetTrade(account, request.ExchangeOrderId, request.Ticker); + } +} diff --git a/src/Managing.Application/Trading/GetTradesCommandHandler.cs b/src/Managing.Application/Trading/GetTradesCommandHandler.cs new file mode 100644 index 0000000..f44a189 --- /dev/null +++ b/src/Managing.Application/Trading/GetTradesCommandHandler.cs @@ -0,0 +1,25 @@ +using Managing.Application.Abstractions.Services; +using Managing.Application.Trading.Commands; +using Managing.Domain.Trades; +using MediatR; + +namespace Managing.Application.Trading +{ + public class GetTradesCommandHandler : IRequestHandler> + { + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + + public GetTradesCommandHandler(IExchangeService exchangeService, IAccountService accountService) + { + _exchangeService = exchangeService; + _accountService = accountService; + } + + public Task> Handle(GetTradesCommand request, CancellationToken cancellationToken) + { + var account = _accountService.GetAccount(request.AccountName, true, false).Result; + return _exchangeService.GetTrades(account, request.Ticker); + } + } +} diff --git a/src/Managing.Application/Trading/OpenPositionCommandHandler.cs b/src/Managing.Application/Trading/OpenPositionCommandHandler.cs new file mode 100644 index 0000000..bac248f --- /dev/null +++ b/src/Managing.Application/Trading/OpenPositionCommandHandler.cs @@ -0,0 +1,128 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Application.Trading.Commands; +using Managing.Domain.Shared.Helpers; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Application.Trading +{ + public class OpenPositionCommandHandler : ICommandHandler + { + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + private readonly ITradingService _tradingService; + + public OpenPositionCommandHandler( + IExchangeService exchangeService, + IAccountService accountService, + ITradingService tradingService) + { + _exchangeService = exchangeService; + _accountService = accountService; + _tradingService = tradingService; + } + + public Task Handle(OpenPositionRequest request) + { + var account = _accountService.GetAccount(request.AccountName, hideSecrets: false, getBalance: false).Result; + if (!request.IsForPaperTrading && !_exchangeService.CancelOrder(account, request.Ticker).Result) + { + throw new Exception($"Not able to close all orders for {request.Ticker}"); + } + + var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator; + var position = new Position(request.AccountName, request.Direction, request.Ticker, request.MoneyManagement, initiator, request.Date); + var balance = request.IsForPaperTrading ? request.Balance.GetValueOrDefault() : _exchangeService.GetBalance(account, request.IsForPaperTrading).Result; + var balanceAtRisk = RiskHelpers.GetBalanceAtRisk(balance, request.MoneyManagement); + + if (balanceAtRisk < 13) + { + throw new Exception($"Try to risk {balanceAtRisk} $ but inferior to minimum to trade"); + } + + var price = request.IsForPaperTrading && request.Price.HasValue ? + request.Price.Value : + _exchangeService.GetPrice(account, request.Ticker, DateTime.Now); + var quantity = balanceAtRisk / price; + var fee = request.IsForPaperTrading ? request.Fee.GetValueOrDefault() : _tradingService.GetFee(account, request.IsForPaperTrading); + + var expectedStatus = GetExpectedStatus(request); + position.Open = TradingPolicies.OpenPosition(expectedStatus).Execute( + () => + { + var openPrice = request.IsForPaperTrading || request.Price.HasValue + ? request.Price.Value + : _exchangeService.GetBestPrice(account, request.Ticker, price, quantity, request.Direction); + + var trade = _exchangeService.OpenTrade( + account, + request.Ticker, + request.Direction, + openPrice, + quantity, + request.MoneyManagement.Leverage, + TradeType.Limit, + isForPaperTrading: request.IsForPaperTrading, + currentDate: request.Date).Result; + + trade.Fee = TradingHelpers.GetFeeAmount(fee, openPrice * quantity, account.Exchange); + return trade; + }); + + + if (IsOpenTradeHandled(position.Open.Status, account.Exchange) && !request.IgnoreSLTP.GetValueOrDefault()) + { + + var closeDirection = request.Direction == TradeDirection.Long ? TradeDirection.Short : TradeDirection.Long; + + // Stop loss + position.StopLoss = _exchangeService.BuildEmptyTrade( + request.Ticker, + RiskHelpers.GetStopLossPrice(request.Direction, position.Open.Price, request.MoneyManagement), + position.Open.Quantity, + closeDirection, + request.MoneyManagement.Leverage, + TradeType.StopLoss, + request.Date, + TradeStatus.PendingOpen); + + position.StopLoss.Fee = TradingHelpers.GetFeeAmount(fee, position.StopLoss.Price * position.StopLoss.Quantity, account.Exchange); + + // Take profit + position.TakeProfit1 = _exchangeService.BuildEmptyTrade( + request.Ticker, + RiskHelpers.GetTakeProfitPrice(request.Direction, position.Open.Price, request.MoneyManagement), + quantity, + closeDirection, + request.MoneyManagement.Leverage, + TradeType.TakeProfit, + request.Date, + TradeStatus.PendingOpen); + + position.TakeProfit1.Fee = TradingHelpers.GetFeeAmount(fee, position.TakeProfit1.Price * position.TakeProfit1.Quantity, account.Exchange); + } + + position.Status = IsOpenTradeHandled(position.Open.Status, account.Exchange) ? position.Status : PositionStatus.Rejected; + _tradingService.InsertPosition(position); + + return Task.FromResult(position); + } + + private static TradeStatus GetExpectedStatus(OpenPositionRequest request) + { + if (request.IsForPaperTrading) + { + return TradeStatus.Filled; + } + + return TradeStatus.Requested; + } + + private static bool IsOpenTradeHandled(TradeStatus tradeStatus, TradingExchanges exchange) + { + return tradeStatus == TradeStatus.Filled + || (exchange == TradingExchanges.Evm && tradeStatus == TradeStatus.Requested); + } + } +} diff --git a/src/Managing.Application/Trading/TradingPolicies.cs b/src/Managing.Application/Trading/TradingPolicies.cs new file mode 100644 index 0000000..20399bc --- /dev/null +++ b/src/Managing.Application/Trading/TradingPolicies.cs @@ -0,0 +1,18 @@ +using Managing.Domain.Trades; +using Polly; +using Polly.Retry; +using static Managing.Common.Enums; + +namespace Managing.Application.Trading; + +public static class TradingPolicies +{ + public static RetryPolicy OpenPosition(TradeStatus tradeStatus) + { + var policy = Policy + .HandleResult(res => res.Status != tradeStatus) + .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3)); + + return policy; + } +} diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs new file mode 100644 index 0000000..47871b5 --- /dev/null +++ b/src/Managing.Application/Trading/TradingService.cs @@ -0,0 +1,352 @@ +using DnsClient.Internal; +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Domain.Accounts; +using Managing.Domain.Scenarios; +using Managing.Domain.Statistics; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using Managing.Domain.Shared.Helpers; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; +using static Managing.Common.Enums; + +namespace Managing.Application.Trading; + +public class TradingService : ITradingService +{ + private readonly ITradingRepository _tradingRepository; + private readonly IExchangeService _exchangeService; + private readonly IAccountService _accountService; + private readonly ICacheService _cacheService; + private readonly IMessengerService _messengerService; + private readonly IStatisticRepository _statisticRepository; + private readonly ILogger _logger; + + public TradingService( + ITradingRepository tradingRepository, + IExchangeService exchangeService, + ILogger logger, + IAccountService accountService, + ICacheService cacheService, + IMessengerService messengerService, + IStatisticRepository statisticRepository) + { + _tradingRepository = tradingRepository; + _exchangeService = exchangeService; + _logger = logger; + _accountService = accountService; + _cacheService = cacheService; + _messengerService = messengerService; + _statisticRepository = statisticRepository; + } + + public void DeleteScenario(string name) + { + _tradingRepository.DeleteScenario(name); + } + + public void DeleteScenarios() + { + _tradingRepository.DeleteScenarios(); + } + + public void DeleteStrategies() + { + _tradingRepository.DeleteStrategies(); + } + + public void DeleteStrategy(string name) + { + _tradingRepository.DeleteStrategy(name); + } + + public Position GetPositionByIdentifier(string identifier) + { + return _tradingRepository.GetPositionByIdentifier(identifier); + } + + public IEnumerable GetPositions(PositionInitiator positionInitiator) + { + return _tradingRepository.GetPositions(positionInitiator); + } + + public IEnumerable GetPositionsByStatus(PositionStatus postionStatus) + { + return _tradingRepository.GetPositionsByStatus(postionStatus); + } + + + + public Scenario GetScenarioByName(string scenario) + { + return _tradingRepository.GetScenarioByName(scenario); + } + + public IEnumerable GetScenarios() + { + return _tradingRepository.GetScenarios(); + } + + public IEnumerable GetStrategies() + { + return _tradingRepository.GetStrategies(); + } + + public Strategy GetStrategyByName(string strategy) + { + return _tradingRepository.GetStrategyByName(strategy); + } + + public void InsertPosition(Position position) + { + _tradingRepository.InsertPosition(position); + } + + public void InsertScenario(Scenario scenario) + { + _tradingRepository.InsertScenario(scenario); + } + + public void InsertSignal(Signal signal) + { + _tradingRepository.InsertSignal(signal); + } + + public void InsertStrategy(Strategy strategy) + { + _tradingRepository.InsertStrategy(strategy); + } + + public async Task ManagePosition(Account account, Position position) + { + var lastPrice = _exchangeService.GetPrice(account, position.Ticker, DateTime.UtcNow); + var quantityInPosition = await _exchangeService.GetQuantityInPosition(account, position.Ticker); + var orders = await _exchangeService.GetOpenOrders(account, position.Ticker); + + if (quantityInPosition > 0) + { + // Position still open + position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.Open.Quantity, lastPrice); + _logger.LogInformation($"Position is still open - PNL : {position.ProfitAndLoss.Realized} $"); + _logger.LogInformation($"Requested trades : {orders.Count}"); + } + else + { + // No quantity in position = SL/TP hit + if (orders.All(o => o.TradeType != TradeType.StopLoss)) + { + // SL hit + _logger.LogInformation($"Stop loss is filled on exchange."); + position.StopLoss.SetStatus(TradeStatus.Filled); + position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.StopLoss.Quantity, position.StopLoss.Price); + _ = _exchangeService.CancelOrder(account, position.Ticker); + } + else if (orders.All(o => o.TradeType != TradeType.TakeProfit)) + { + // TP Hit + if (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit2 != null) + { + position.TakeProfit2.SetStatus(TradeStatus.Filled); + position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.TakeProfit2.Quantity, position.TakeProfit2.Price); + _logger.LogInformation($"TakeProfit 2 is filled on exchange."); + } + else + { + position.TakeProfit1.SetStatus(TradeStatus.Filled); + position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.TakeProfit1.Quantity, position.TakeProfit1.Price); + _logger.LogInformation($"TakeProfit 1 is filled on exchange."); + } + } + else + { + _logger.LogInformation($"Position closed manually or forced close by exchange because quantity in position is below 0."); + position.Status = PositionStatus.Finished; + + if (orders.Any()) await _exchangeService.CancelOrder(account, position.Ticker); + } + } + + return position; + } + + public void UpdateFee(TradingExchanges exchange) + { + var lastFee = _tradingRepository.GetFee(exchange); + var account = _accountService.GetAccounts(false, false).FirstOrDefault(a => a.Exchange == exchange); + if (lastFee != null) + { + if (DateTime.UtcNow.AddHours(-6) >= lastFee.LastUpdate) + { + lastFee.Cost = _exchangeService.GetFee(account); + lastFee.LastUpdate = DateTime.UtcNow; + _tradingRepository.UpdateFee(lastFee); + } + } + else + { + lastFee = new Fee + { + Cost = _exchangeService.GetFee(account), + Exchange = exchange, + LastUpdate = DateTime.UtcNow + }; + + _tradingRepository.InsertFee(lastFee); + } + } + + public decimal GetFee(Account account, bool isForPaperTrading = false) + { + if (isForPaperTrading && account.Exchange != TradingExchanges.Evm) + { + return 0.000665M; + } + + return _cacheService.GetOrSave($"Fee-{account.Exchange}", () => + { + return _tradingRepository.GetFee(TradingExchanges.Evm).Cost; + }, TimeSpan.FromHours(2)); + } + + public void UpdatePosition(Position position) + { + _tradingRepository.UpdatePosition(position); + } + + public IEnumerable GetPositions() + { + var positions = new List(); + positions.AddRange(GetPositionsByStatus(PositionStatus.New)); + positions.AddRange(GetPositionsByStatus(PositionStatus.Filled)); + positions.AddRange(GetPositionsByStatus(PositionStatus.PartiallyFilled)); + return positions; + } + + + public async Task WatchTrader() + { + var availableTickers = new List { Ticker.BTC, Ticker.ETH, Ticker.UNI, Ticker.LINK }; + var watchAccount = GetTradersWatch(); + var key = $"AccountsQuantityInPosition"; + var aqip = _cacheService.GetValue>(key); + + if (aqip == null) + { + aqip = GetAccountsQuantityInPosition(watchAccount); + } + else + { + foreach (var a in watchAccount.Where(w => !aqip.Any(a => a.Account.Address == w.Address))) + { + var newAccount = SetupFollowUp(a); + aqip.Add(newAccount); + } + + foreach (var a in aqip) + { + await ManageTrader(a, availableTickers); + } + + } + + _cacheService.SaveValue(key, aqip, TimeSpan.FromMinutes(10)); + } + + public IEnumerable GetTradersWatch() + { + var watchAccount = _statisticRepository.GetBestTraders(); + var customWatchAccount = _accountService.GetAccounts(true, false).Where(a => a.Type == AccountType.Watch).ToList().MapToTraders(); + watchAccount.AddRange(customWatchAccount.Where(a => !watchAccount.Any(w => w.Address.Equals(a.Address, StringComparison.InvariantCultureIgnoreCase)))); + return watchAccount; + } + + private async Task ManageTrader(TraderFollowup a, List tickers) + { + var shortAddress = a.Account.Address.Substring(0, 6); + + foreach (var ticker in tickers) + { + try + { + var newTrade = await _exchangeService.GetTrade(a.Account.Address, "", ticker); + var oldTrade = a.Trades.SingleOrDefault(t => t.Ticker == ticker); + if (newTrade == null) + { + if (oldTrade != null) + { + _logger.LogInformation($"[{shortAddress}][{ticker}] Trader previously got a position open but the position was close by trader"); + await _messengerService.SendClosedPosition(a.Account.Address, oldTrade); + a.Trades.Remove(oldTrade); + } + } + else if ((newTrade != null && oldTrade == null) || (newTrade.Quantity > oldTrade.Quantity)) + { + _logger.LogInformation($"[{shortAddress}][{ticker}] Trader increase {newTrade.Direction} by {newTrade.Quantity - (oldTrade?.Quantity ?? 0)} with leverage {newTrade.Leverage} at {newTrade.Price} leverage."); + + var index = a.Trades.IndexOf(oldTrade); + if (index != -1) + { + a.Trades[index] = newTrade; + } + else + { + a.Trades.Add(newTrade); + } + + // Open position + await _messengerService.SendIncreasePosition(a.Account.Address, newTrade, "Test6", oldTrade); + // Save position to cache + } + else if (newTrade.Quantity < oldTrade.Quantity && newTrade.Quantity > 0) + { + var decreaseAmount = oldTrade.Quantity - newTrade.Quantity; + var index = a.Trades.IndexOf(oldTrade); + a.Trades[index] = newTrade; + _logger.LogInformation($"[{a.Account.Address.Substring(0, 6)}][{ticker}] Trader decrease position but didnt close it {decreaseAmount}"); + await _messengerService.SendDecreasePosition(a.Account.Address, newTrade, decreaseAmount); + } + else + { + _logger.LogInformation($"[{shortAddress}][{ticker}] No change - Quantity still {newTrade.Quantity}"); + } + } + catch (Exception ex) + { + _logger.LogError($"[{shortAddress}][{ticker}] Impossible to fetch trader"); + } + } + } + + private List GetAccountsQuantityInPosition(IEnumerable watchAccount) + { + var result = new List (); + foreach (var account in watchAccount) + { + var trader = SetupFollowUp(account); + result.Add(trader); + } + + return result; + } + + private static TraderFollowup SetupFollowUp(Trader account) + { + var trader = new TraderFollowup + { + Account = account, + Trades = new List(), + PositionIdentifiers = new List() + }; + + return trader; + } + + public class TraderFollowup + { + public Trader Account { get; set; } + public List Trades { get; set; } + public List PositionIdentifiers { get; set; } + } +} diff --git a/src/Managing.Application/Users/UserService.cs b/src/Managing.Application/Users/UserService.cs new file mode 100644 index 0000000..c11d719 --- /dev/null +++ b/src/Managing.Application/Users/UserService.cs @@ -0,0 +1,88 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Domain.Accounts; +using Managing.Domain.Users; + +namespace Managing.Application.Users; + +public class UserService : IUserService +{ + private readonly IEvmManager _evmManager; + private readonly IUserRepository _userRepository; + private readonly IAccountService _accountService; + + public UserService( + IEvmManager evmManager, + IUserRepository userRepository, + IAccountService accountService) + { + _evmManager = evmManager; + _userRepository = userRepository; + _accountService = accountService; + } + + public async Task Authenticate(string name, string address, string message, string signature) + { + var recoveredAddress = _evmManager.VerifySignature(signature, message); + + if (recoveredAddress == null || !recoveredAddress.Equals(address)) + throw new Exception("Address not corresponding"); + + // Check if account exist + var account = await _accountService.GetAccountByKey(recoveredAddress, true, false); + var user = await _userRepository.GetUserByNameAsync(name); + + if (account != null && user != null) + { + // User and account found + user.Accounts = _accountService.GetAccountsByUser(user).ToList(); + + if (!user.Name.Equals(name)) + throw new Exception("Name not corresponding"); + + return user; + } + else + { + // No account and no + account = new Account + { + Name = $"Auth-{new Random().Next(1, 99)}", + Key = recoveredAddress, + Secret = "", + Exchange = Common.Enums.TradingExchanges.Evm, + Type = Common.Enums.AccountType.Auth, + }; + + if (user != null) + { + _ = await _accountService.CreateAccount(user, account); + user.Accounts = _accountService.GetAccountsByUser(user).ToList(); + return user; + } + else + { + // No user found, we create one + // Create user if not existing + user = new User() + { + Name = name, + Accounts = new List { account }, + }; + + _ = await _accountService.CreateAccount(user, account); + await _userRepository.InsertUserAsync(user); + } + } + + return user; + } + + public async Task GetUserByAddressAsync(string address) + { + var account = await _accountService.GetAccountByKey(address, true, false); + var user = await _userRepository.GetUserByNameAsync(account.User.Name); + user.Accounts = _accountService.GetAccountsByUser(user).ToList(); + return user; + } +} diff --git a/src/Managing.Application/Workflows/FlowFactory.cs b/src/Managing.Application/Workflows/FlowFactory.cs new file mode 100644 index 0000000..7d04213 --- /dev/null +++ b/src/Managing.Application/Workflows/FlowFactory.cs @@ -0,0 +1,51 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Application.Workflows.Flows.Feeds; +using Managing.Application.Workflows.Flows.Trading; +using Managing.Domain.Workflows; +using Managing.Domain.Workflows.Synthetics; +using static Managing.Common.Enums; + +namespace Managing.Application.Workflows; + +public class FlowFactory : IFlowFactory +{ + private readonly IExchangeService _exchangeService; + private readonly ICacheService _cacheService; + private readonly ITradingService _tradingService; + private readonly IAccountService _accountService; + + public FlowFactory(IExchangeService exchangeService, ICacheService cacheService, ITradingService tradingService, IAccountService accountService) + { + _exchangeService = exchangeService; + _cacheService = cacheService; + _tradingService = tradingService; + _accountService = accountService; + } + + public IFlow BuildFlow(SyntheticFlow request) + { + IFlow flow = request.Type switch + { + FlowType.FeedTicker => new FeedTicker(_exchangeService), + FlowType.RsiDivergence => new RsiDiv(), + FlowType.OpenPosition => new OpenPosition(_exchangeService, _cacheService, _accountService, _tradingService), + _ => throw new NotImplementedException(), + }; + + flow.Children = new List(); + flow.Parameters = new List(); + + foreach (var parameter in request.Parameters) + { + if (!flow.Parameters.Any(p => p.Name == parameter.Name)) { + flow.Parameters.Add(new FlowParameter + { + Name = parameter.Name, + Value = parameter.Value + }); + } + } + return flow; + } +} diff --git a/src/Managing.Application/Workflows/Flows/Feeds/FeedTicker.cs b/src/Managing.Application/Workflows/Flows/Feeds/FeedTicker.cs new file mode 100644 index 0000000..f0087bb --- /dev/null +++ b/src/Managing.Application/Workflows/Flows/Feeds/FeedTicker.cs @@ -0,0 +1,64 @@ +using Managing.Application.Abstractions.Services; +using Managing.Domain.Workflows; +using Newtonsoft.Json; +using static Managing.Common.Enums; + +namespace Managing.Application.Workflows.Flows.Feeds; + +public class FeedTicker : FlowBase +{ + public override List Children { get; set; } + public override List Parameters { get; set; } + public override Guid ParentId { get; } + public override Guid Id { get; } + public override string Output { get; set; } + public override string Name => "Feed Ticker"; + public override FlowType Type => FlowType.FeedTicker; + public override string Description => "This flow will take a feed and output the candles"; + public FeedTickerParameters FeedTickerParameters { get; set; } + public override List AcceptedInputs => new(); + public override List OutputTypes => new() { FlowOutput.Candles }; + private readonly IExchangeService _exchangeService; + + public FeedTicker(IExchangeService exchangeService) + { + _exchangeService = exchangeService; + } + + public async override Task Execute(string input) + { + MapParameters(); + var candles = await _exchangeService.GetCandlesInflux(FeedTickerParameters.Exchange, FeedTickerParameters.Ticker, DateTime.Now.AddDays(-11), FeedTickerParameters.Timeframe); + + Output = JsonConvert.SerializeObject(candles.ToHashSet()); + + if(Children != null && Children.Count > 0) + { + foreach (var child in Children) + { + await child.Execute(Output); + } + } + } + + public override void MapParameters() + { + FeedTickerParameters = new FeedTickerParameters(); + foreach (var param in Parameters) + { + if (param.Name == nameof(FeedTickerParameters.Ticker)) + FeedTickerParameters.Ticker = (Ticker)Enum.Parse(typeof(Ticker), param.Value); + if (param.Name == nameof(FeedTickerParameters.Exchange)) + FeedTickerParameters.Exchange = (TradingExchanges)Enum.Parse(typeof(TradingExchanges), param.Value); + if (param.Name == nameof(FeedTickerParameters.Timeframe)) + FeedTickerParameters.Timeframe = (Timeframe)Enum.Parse(typeof(Timeframe), param.Value); + } + } +} + +public class FeedTickerParameters +{ + public TradingExchanges Exchange { get; set; } + public Ticker Ticker { get; set; } + public Timeframe Timeframe { get; set; } +} diff --git a/src/Managing.Application/Workflows/Flows/Strategies/RsiDiv.cs b/src/Managing.Application/Workflows/Flows/Strategies/RsiDiv.cs new file mode 100644 index 0000000..3948267 --- /dev/null +++ b/src/Managing.Application/Workflows/Flows/Strategies/RsiDiv.cs @@ -0,0 +1,65 @@ +using Managing.Domain.Candles; +using Managing.Domain.Strategies; +using Managing.Domain.Workflows; +using Newtonsoft.Json; +using static Managing.Common.Enums; + +namespace Managing.Application.Workflows.Flows.Feeds; + +public class RsiDiv : FlowBase +{ + public override List Children { get; set; } + public override List Parameters { get; set; } + public override Guid ParentId { get; } + public override Guid Id { get; } + public override string Output { get; set; } + public override string Name => "Rsi Divergence"; + public override string Description => "This flow will take a feed of candle an run the RSI Divergence"; + public override FlowType Type => FlowType.RsiDivergence; + public override List AcceptedInputs => new() { FlowOutput.Candles }; + public override List OutputTypes => new() { FlowOutput.Signal }; + public RsiDivParameters RsiDivParameters { get; set; } + + public async override Task Execute(string input) + { + if (string.IsNullOrEmpty(input)) + { + throw new ArgumentException($"'{nameof(input)}' cannot be null or empty.", nameof(input)); + } + + MapParameters(); + var candles = JsonConvert.DeserializeObject>(input); + + var strategy = new RSIDivergenceStrategy(Name, RsiDivParameters.Timeframe, RsiDivParameters.Period); + strategy.UpdateCandles(candles); + strategy.Run(); + + Output = JsonConvert.SerializeObject(strategy.Signals); + + if(Children != null && Children.Count > 0) + { + foreach (var child in Children) + { + await child.Execute(Output); + } + } + } + + public override void MapParameters() + { + RsiDivParameters = new RsiDivParameters(); + foreach (var param in Parameters) + { + if (param.Name == nameof(RsiDivParameters.Period)) + RsiDivParameters.Period = int.Parse(param.Value); + if (param.Name == nameof(RsiDivParameters.Timeframe)) + RsiDivParameters.Timeframe = (Timeframe)Enum.Parse(typeof(Timeframe), param.Value); + } + } +} + +public class RsiDivParameters +{ + public int Period { get; set; } + public Timeframe Timeframe { get; set; } +} diff --git a/src/Managing.Application/Workflows/Flows/Trading/OpenPosition.cs b/src/Managing.Application/Workflows/Flows/Trading/OpenPosition.cs new file mode 100644 index 0000000..67eda2b --- /dev/null +++ b/src/Managing.Application/Workflows/Flows/Trading/OpenPosition.cs @@ -0,0 +1,230 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Application.Accounts; +using Managing.Application.Shared; +using Managing.Application.Trading.Commands; +using Managing.Application.Trading; +using Managing.Domain.Accounts; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using Managing.Domain.Workflows; +using Newtonsoft.Json; +using static Managing.Common.Enums; +using Managing.Domain.Candles; +using Managing.Application.Abstractions.Repositories; + +namespace Managing.Application.Workflows.Flows.Trading; + +public class OpenPosition : FlowBase +{ + public override List Children { get; set; } + public override List Parameters { get; set; } + public override Guid ParentId { get; } + public override Guid Id { get; } + public override string Output { get; set; } + public override string Name => "Open Position"; + public override FlowType Type => FlowType.OpenPosition; + public override string Description => "This flow will open a position for a given signal"; + public OpenPositionParameters OpenPositionParameters { get; set; } + public override List AcceptedInputs => new() { FlowOutput.Signal, FlowOutput.MoneyManagement }; + public override List OutputTypes => new() { FlowOutput.Position }; + private readonly IExchangeService _exchangeService; + private readonly ICacheService _cacheService; + private readonly IAccountService _accountService; + private readonly ITradingService _tradingService; + private readonly ISettingsRepository _settingsRepository; + private readonly IMessengerService _messengerService; + + private readonly string POSITIONS_KEY = "positions"; + private readonly string ACCOUNT_KEY = "account"; + private readonly string CANDLES_KEY = "candles"; + private readonly string SIGNALS_KEY = "signals"; + private readonly string FEE_KEY = "fee"; + private readonly string WALLET_BALANCES = "wallet-balance"; + private decimal Fee { get; set; } + + + public OpenPosition( + IExchangeService exchangeService, + ICacheService cacheService, + IAccountService accountService, + ITradingService tradingService) + { + _exchangeService = exchangeService; + _cacheService = cacheService; + _accountService = accountService; + _tradingService = tradingService; + } + + public async override Task Execute(string input) + { + MapParameters(); + var signal = JsonConvert.DeserializeObject(input); + var Candles = JsonConvert.DeserializeObject>(_cacheService.GetValue(CANDLES_KEY)); + var Account = JsonConvert.DeserializeObject(_cacheService.GetValue(ACCOUNT_KEY)); + var Positions = JsonConvert.DeserializeObject>(_cacheService.GetValue(POSITIONS_KEY)); + var Signals = JsonConvert.DeserializeObject>(_cacheService.GetValue(POSITIONS_KEY)); + + Fee = _cacheService.GetOrSave(FEE_KEY, () => + { + return _tradingService.GetFee(Account, OpenPositionParameters.IsForBacktest); + }, TimeSpan.FromDays(1)); + + await ExecuteOpenPosition(signal, Positions, Signals, Candles, Account); + + _cacheService.SaveValue(POSITIONS_KEY, JsonConvert.SerializeObject(Positions)); + _cacheService.SaveValue(SIGNALS_KEY, JsonConvert.SerializeObject(Signals)); + + if (Children != null && Children.Count > 0) + { + foreach (var child in Children) + { + await child.Execute(Output); + } + } + } + + private async Task ExecuteOpenPosition(Signal signal, List positions, HashSet signals, HashSet candles, Account account) + { + // Check if a position is already open + var openedPosition = positions.FirstOrDefault(p => p.Status == PositionStatus.Filled + && p.SignalIdentifier != signal.Identifier); + + var lastPrice = OpenPositionParameters.IsForBacktest ? candles.Last().Close : _exchangeService.GetPrice(account, signal.Ticker, DateTime.UtcNow); + + // If position open + if (openedPosition != null) + { + var previousSignal = signals.First(s => s.Identifier == openedPosition.SignalIdentifier); + + // Check if signal is the opposite side => flip the position + if (openedPosition.OriginDirection == signal.Direction) + { + // An operation is already open for the same direction + //await LogInformation($"Signal {signal.Identifier} try to open a position but {previousSignal.Identifier} is already open for the same direction"); + signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; + } + else + { + // An operation is already open for the opposite direction + // ==> Flip the position + if (OpenPositionParameters.FlipPosition) + { + //await LogInformation("Try to flip the position because of an opposite direction signal"); + //await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true); + positions.FirstOrDefault(s => s.Identifier == previousSignal.Identifier).Status = PositionStatus.Flipped; + await ExecuteOpenPosition(signal, positions, signals, candles, account); + + //await LogInformation($"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$"); + } + else + { + //await LogWarning($"A position is already open for signal {previousSignal.Identifier}. Position flipping is currently not enable, the position will not be flipped."); + signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; + } + } + } + else + { + if (!CanOpenPosition(signal, positions, signals, candles)) + { + //await LogInformation("Tried to open position but last position was a loss. Wait for an opposition direction side or wait x candles to open a new position"); + signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; + return; + } + //await LogInformation($"Open position - Date: {signal.Date:T} - SignalIdentifier : {signal.Identifier} - Strategie : {signal.StrategyType}"); + + try + { + var moneyManagement = await _settingsRepository.GetMoneyManagement(OpenPositionParameters.MoneyManagementName); + var WalletBalances = JsonConvert.DeserializeObject>(_cacheService.GetValue(WALLET_BALANCES)); + + var command = new OpenPositionRequest( + OpenPositionParameters.AccountName, + moneyManagement, + signal.Direction, + signal.Ticker, + PositionInitiator.Bot, + signal.Date, + OpenPositionParameters.IsForBacktest, + lastPrice, + balance: WalletBalances.LastOrDefault().Value, + fee: Fee); + + var position = await new OpenPositionCommandHandler(_exchangeService, _accountService, _tradingService) + .Handle(command); + + if (position != null) + { + if (position.Open.Status != TradeStatus.Cancelled) + { + position.SignalIdentifier = signal.Identifier; + positions.Add(position); + signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.PositionOpen; + + if (!OpenPositionParameters.IsForBacktest) + { + await _messengerService.SendPosition(position); + } + Output = JsonConvert.SerializeObject(position); + //Logger.LogInformation($"Position requested"); + } + else + { + positions.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = PositionStatus.Rejected; + signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; + } + } + } + catch (Exception ex) + { + signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; + //await LogWarning($"Cannot open trade : {ex.Message}"); + } + } + + } + + private bool CanOpenPosition(Signal signal, List positions, HashSet signals, HashSet candles) + { + if (positions.Count == 0) + return true; + + var lastPosition = positions.LastOrDefault(p => p.IsFinished() + && p.SignalIdentifier != signal.Identifier + && p.ProfitAndLoss.Realized < 0 + && p.OriginDirection == signal.Direction); + + if (lastPosition == null) + return true; + + var tenCandleAgo = candles.TakeLast(10).First(); + var positionSignal = signals.FirstOrDefault(s => s.Identifier == lastPosition.SignalIdentifier); + + return positionSignal.Date < tenCandleAgo.Date; + } + + + public override void MapParameters() + { + OpenPositionParameters = new OpenPositionParameters(); + foreach (var param in Parameters) + { + if (param.Name == nameof(OpenPositionParameters.AccountName)) + OpenPositionParameters.AccountName = param.Value; + if (param.Name == nameof(OpenPositionParameters.MoneyManagementName)) + OpenPositionParameters.MoneyManagementName = param.Value; + if (param.Name == nameof(OpenPositionParameters.FlipPosition)) + OpenPositionParameters.FlipPosition = bool.Parse(param.Value); + } + } +} + +public class OpenPositionParameters +{ + public string MoneyManagementName { get; set; } + public string AccountName { get; set; } + public bool IsForBacktest { get; set; } + public bool FlipPosition { get; set; } +} diff --git a/src/Managing.Application/Workflows/WorkflowService.cs b/src/Managing.Application/Workflows/WorkflowService.cs new file mode 100644 index 0000000..77f3e95 --- /dev/null +++ b/src/Managing.Application/Workflows/WorkflowService.cs @@ -0,0 +1,126 @@ +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Application.Workflows.Flows.Feeds; +using Managing.Application.Workflows.Flows.Trading; +using Managing.Domain.Workflows; +using Managing.Domain.Workflows.Synthetics; + +namespace Managing.Application.Workflows; + +public class WorkflowService : IWorkflowService +{ + private readonly IWorkflowRepository _workflowRepository; + private readonly IExchangeService _exchangeService; + private readonly IFlowFactory _flowFactory; + private readonly ICacheService _cacheService; + private readonly ITradingService _tradingService; + private readonly IAccountService _accountService; + + public WorkflowService( + IWorkflowRepository workflowRepository, + IExchangeService exchangeService, + IFlowFactory flowFactory, + ICacheService cacheService, + ITradingService tradingService, + IAccountService accountService) + { + _workflowRepository = workflowRepository; + _exchangeService = exchangeService; + _flowFactory = flowFactory; + _cacheService = cacheService; + _tradingService = tradingService; + _accountService = accountService; + } + + public async Task InsertOrUpdateWorkflow(SyntheticWorkflow workflowRequest) + { + var previousWorkflow = await _workflowRepository.GetWorkflow(workflowRequest.Name); + + try + { + if (previousWorkflow != null) + { + await _workflowRepository.UpdateWorkflow(workflowRequest); + } + else + { + await _workflowRepository.InsertWorkflow(workflowRequest); + } + } + catch (Exception ex) + { + + throw; + } + + return Map(workflowRequest); + } + + private Workflow Map(SyntheticWorkflow workflowRequest) + { + var workflow = new Workflow + { + Name = workflowRequest.Name, + Usage = workflowRequest.Usage, + Description = workflowRequest.Description, + Flows = new List() + }; + + // Add first flow that dont have any parent + var firstFlow = workflowRequest.Flows.SingleOrDefault(f => string.IsNullOrEmpty(f.ParentId)); + + if (firstFlow != null) + { + var flow = Build(workflowRequest.Flows, firstFlow); + workflow.Flows.Add(flow); + } + else + { + // TODO : Throw exception + throw new Exception("No first flow found"); + } + + return workflow; + } + + private IFlow Build(List flows, SyntheticFlow firstFlow) + { + var flow = _flowFactory.BuildFlow(firstFlow); + + foreach (var flowRequest in flows.Where(f => f.ParentId == firstFlow.Id)) + { + flow.Children.Add(Build(flows, flowRequest)); + } + + return flow; + } + + public IEnumerable GetWorkflows() + { + return _workflowRepository.GetWorkflows(); + } + + public bool DeleteWorkflow(string name) + { + return _workflowRepository.DeleteWorkflow(name); + } + + public async Task GetWorkflow(string name) + { + return Map(await _workflowRepository.GetWorkflow(name)); + } + + public Task> GetAvailableFlows() + { + var availableFlows = new List + { + new FeedTicker(_exchangeService), + new RsiDiv(), + new OpenPosition(_exchangeService, _cacheService, _accountService, _tradingService) + }; + + return Task.FromResult(availableFlows.AsEnumerable()); + } + +} diff --git a/src/Managing.Bootstrap/ApiBootstrap.cs b/src/Managing.Bootstrap/ApiBootstrap.cs new file mode 100644 index 0000000..ae46644 --- /dev/null +++ b/src/Managing.Bootstrap/ApiBootstrap.cs @@ -0,0 +1,157 @@ +using FluentValidation; +using Managing.Application.Shared.Behaviours; +using MediatR; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; +using Managing.Infrastructure.Exchanges; +using Managing.Infrastructure.Storage; +using Managing.Infrastructure.Databases.MongoDb; +using Microsoft.Extensions.Options; +using Managing.Application.Trading; +using Managing.Infrastructure.Messengers.Discord; +using Managing.Infrastructure.Evm; +using Discord.WebSocket; +using Microsoft.AspNetCore.Hosting; +using Discord.Commands; +using Managing.Application.Backtesting; +using Managing.Application.Scenarios; +using Managing.Application.MoneyManagements; +using Managing.Application.Accounts; +using Managing.Application.Abstractions; +using Managing.Application.Shared; +using Managing.Infrastructure.Databases; +using Managing.Infrastructure.Databases.Abstractions; +using Managing.Infrastructure.Databases.InfluxDb.Models; +using Managing.Infrastructure.Databases.InfluxDb; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Exchanges.Abstractions; +using Managing.Infrastructure.Exchanges.Exchanges; +using Managing.Application.Users; +using Managing.Infrastructure.Evm.Subgraphs; +using Managing.Application.ManageBot.Commands; +using Managing.Application.Trading.Commands; +using Managing.Domain.Trades; +using Managing.Application.Workers.Abstractions; +using Managing.Application.Workers; +using Binance.Net.Clients; +using Binance.Net.Interfaces.Clients; +using Managing.Infrastructure.Evm.Services; +using Managing.Application.Workflows; +using Managing.Application.Bots.Base; + +namespace Managing.Bootstrap; + +public static class ApiBootstrap +{ + + private static readonly Assembly ApplicationAssembly = typeof(StartBotCommand).GetTypeInfo().Assembly; + + public static IServiceCollection RegisterApiDependencies(this IServiceCollection services, IConfiguration configuration) + { + return services + .AddApplication() + .AddInfrastructure(configuration) + .AddFluentValidation() + .AddMediatR(); + } + + private static IServiceCollection AddApplication(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient, OpenPositionCommandHandler>(); + services.AddTransient, ClosePositionCommandHandler>(); + + return services; + } + + private static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) + { + // Database + services.AddSingleton(sp => + sp.GetRequiredService>().Value); + + services.AddTransient(typeof(IMongoRepository<>), typeof(MongoRepository<>)); + + services.AddSingleton(sp => + sp.GetRequiredService>().Value); + + // Evm + services.AddGbcFeed(); + services.AddUniswapV2(); + services.AddChainlink(); + services.AddChainlinkGmx(); + services.AddSingleton(); + + // Repositories + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + // Cache + services.AddDistributedMemoryCache(); + services.AddTransient(); + services.AddTransient(); + + // Processors + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + // Services + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); + services.AddSingleton(); + + // Stream + services.AddSingleton(); + services.AddSingleton(); + + return services; + } + + private static IServiceCollection AddMediatR(this IServiceCollection services) + { + return services.AddMediatR(AppDomain.CurrentDomain.GetAssemblies()) + .AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)) + .AddTransient(typeof(IUnhandledExceptionBehaviour<,>), typeof(UnhandledExceptionBehaviour<,>)); + } + + private static IServiceCollection AddFluentValidation(this IServiceCollection services) + { + return services.AddValidatorsFromAssembly(ApplicationAssembly); + } + + public static IWebHostBuilder SetupDiscordBot(this IWebHostBuilder builder) + { + return builder.ConfigureServices(services => + { + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddHostedService(); + }); + } +} diff --git a/src/Managing.Bootstrap/Managing.Bootstrap.csproj b/src/Managing.Bootstrap/Managing.Bootstrap.csproj new file mode 100644 index 0000000..b63fd55 --- /dev/null +++ b/src/Managing.Bootstrap/Managing.Bootstrap.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Managing.Bootstrap/WorkersBootstrap.cs b/src/Managing.Bootstrap/WorkersBootstrap.cs new file mode 100644 index 0000000..b6676e1 --- /dev/null +++ b/src/Managing.Bootstrap/WorkersBootstrap.cs @@ -0,0 +1,117 @@ +using Binance.Net.Clients; +using Binance.Net.Interfaces.Clients; +using Kraken.Net.Clients; +using Kraken.Net.Interfaces.Clients; +using Managing.Application.Abstractions; +using Managing.Application.Accounts; +using Managing.Application.Workers; +using Managing.Application.Workers.Abstractions; +using Managing.Infrastructure.Exchanges; +using Managing.Infrastructure.Databases; +using Managing.Infrastructure.Databases.Abstractions; +using Managing.Infrastructure.Databases.InfluxDb.Models; +using Managing.Infrastructure.Databases.MongoDb; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Managing.Infrastructure.Databases.InfluxDb; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Evm.Subgraphs; +using Managing.Infrastructure.Evm; +using Managing.Infrastructure.Storage; +using Managing.Infrastructure.Exchanges.Abstractions; +using Managing.Infrastructure.Exchanges.Exchanges; +using Managing.Application.Trading; +using Managing.Application.Backtesting; +using Managing.Application.MoneyManagements; +using Managing.Application.Scenarios; +using Managing.Application.Shared; +using Managing.Infrastructure.Messengers.Discord; +using Managing.Application.Trading.Commands; +using Managing.Domain.Trades; +using Managing.Infrastructure.Evm.Services; +using Managing.Application.Bots.Base; + +namespace Managing.Bootstrap; + +public static class WorkersBootstrap +{ + public static IServiceCollection RegisterWorkersDependencies(this IServiceCollection services, IConfiguration configuration) + { + return services + .AddApplication() + .AddInfrastructure(configuration); + } + + private static IServiceCollection AddApplication(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddTransient, OpenPositionCommandHandler>(); + services.AddTransient, ClosePositionCommandHandler>(); + + return services; + } + + private static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) + { + // Database + services.AddSingleton(sp => + sp.GetRequiredService>().Value); + services.AddTransient(typeof(IMongoRepository<>), typeof(MongoRepository<>)); + + services.AddSingleton(sp => + sp.GetRequiredService>().Value); + + services.AddTransient(); + + // Evm + services.AddUniswapV2(); + services.AddGbcFeed(); + services.AddChainlink(); + services.AddChainlinkGmx(); + services.AddSingleton(); + + // Repositories + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + // Cache + services.AddDistributedMemoryCache(); + services.AddTransient(); + services.AddTransient(); + + // Processors + services.AddTransient(); + + // Web Clients + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); + services.AddSingleton(); + + // Messengers + services.AddSingleton(); + services.AddSingleton(); + + return services; + } +} diff --git a/src/Managing.Common/Constants.cs b/src/Managing.Common/Constants.cs new file mode 100644 index 0000000..a91688a --- /dev/null +++ b/src/Managing.Common/Constants.cs @@ -0,0 +1,39 @@ +namespace Managing.Common +{ + public class Constants + { + public class DiscordSlashCommand + { + public const string Leaderboard = "leaderboard"; + public const string Noobiesboard = "noobiesboard"; + public const string LeaderboardPosition = "leaderboardposition"; + } + + public class DiscordButtonAction + { + public const string OpenPosition = "openposition"; + public const string ClosePosition = "closeposition"; + public const string CopyPosition = "ctp"; + } + + public class Databases + { + public const string InfluxDb = "InfluxDb"; + public const string MongoDb = "ManagingDatabase"; + } + + public class Chains + { + public const string Ethereum = "Ethereum"; + public const string Arbitrum = "Arbitrum"; + public const string ArbitrumGoerli = "ArbitrumGoerli"; + public const string Goerli = "Goerli"; + } + + public class Stablecoins + { + public const string Usd = "USD"; + public const string Usdt = "USDT"; + } + } +} diff --git a/src/Managing.Common/Enums.cs b/src/Managing.Common/Enums.cs new file mode 100644 index 0000000..6bf4d83 --- /dev/null +++ b/src/Managing.Common/Enums.cs @@ -0,0 +1,343 @@ +namespace Managing.Common; + +public static class Enums +{ + public enum TradingExchanges + { + Binance, + Kraken, + Ftx, + Evm + } + + public enum GmxOrderType + { + Increase, + Decrease, + Swap + } + + public enum SubgraphProvider + { + UniswapV2, + ChainlinkPrice, + ChainlinkGmx, + Gbc + } + + public enum WorkerAction + { + Start, + Stop + } + + public enum AccountType + { + Cex, + Trader, + Watch, + Auth + } + + public enum BotType + { + SimpleBot, + ScalpingBot, + FlippingBot + } + + public enum StrategyType + { + RsiDivergence, + RsiDivergenceConfirm, + MacdCross, + EmaCross, + ThreeWhiteSoldiers, + SuperTrend, + ChandelierExit, + EmaTrend, + Composite, + StochRsiTrend, + Stc, + StDev + } + + public enum SignalType + { + Signal, + Trend, + Context + } + + public enum BotStatus + { + Down, + Starting, + Up + } + + public enum SignalStatus + { + WaitingForPosition, + PositionOpen, + Expired + } + + public enum Confidence + { + Low, + Medium, + High, + None + } + + public enum TradeDirection + { + None, + Short, + Long + } + + public enum RiskLevel + { + Low, + Medium, + High, + Adaptive + } + + public enum TradeType + { + // + // Summary: + // Limit order + Limit = 0, + // + // Summary: + // Symbol order + Market = 1, + // + // Summary: + // Stop market order + StopMarket = 2, + // + // Summary: + // Stop limit order + StopLimit = 3, + // + // Summary: + // Stop loss order + StopLoss = 4, + // + // Summary: + // Take profit order + TakeProfit = 5, + // + // Summary: + // Stop loss profit order + StopLossProfit = 6, + // + // Summary: + // Stop loss profit limit order + StopLossProfitLimit = 7, + // + // Summary: + // Stop loss limit order + StopLossLimit = 8, + // + // Summary: + // Take profit limit order + TakeProfitLimit = 9, + // + // Summary: + // Trailing stop order + TrailingStop = 10, + // + // Summary: + // Trailing stop limit order + TrailingStopLimit = 11, + // + // Summary: + // Stop loss and limit order + StopLossAndLimit = 12, + // + // Summary: + // Settle position + SettlePosition = 13 + } + + public enum TradeStatus + { + PendingOpen = 0, + Requested = 1, + Cancelled = 2, + Filled = 3, + } + + public enum PositionStatus + { + New = 0, + Canceled = 1, + Rejected = 2, + Updating = 3, + PartiallyFilled = 4, + Filled = 5, + Flipped = 6, + Finished = 7 + } + + public enum PositionInitiator + { + PaperTrading, + Bot, + User, + CopyTrading + } + + public enum Timeframe + { + /// + /// 5m + /// + FiveMinutes, + /// + /// 15m + /// + FifteenMinutes, + /// + /// 30m + /// + ThirtyMinutes, + /// + /// 1h + /// + OneHour, + /// + /// 4h + /// + FourHour, + /// + /// 1d + /// + OneDay, + } + + public enum Ticker + { + ADA, + APE, + ALICE, + ALGO, + ATOM, + AVAX, + AXS, + BAT, + BNB, + BTC, + BAL, + C98, + CHR, + CHZ, + COMP, + CRO, + CRV, + CVC, + DEFI, + DOGE, + DOT, + DYDX, + ENS, + ETC, + ETH, + FIL, + FLM, + FTM, + GALA, + GMT, + GMX, + GRT, + HNT, + IMX, + JASMY, + KAVA, + KSM, + LDO, + LINK, + LOOKS, + LRC, + LTC, + MANA, + MATIC, + MKR, + NEAR, + NEO, + OMG, + ONE, + ONT, + QTUM, + REEF, + REN, + ROSE, + RSR, + RUNE, + SAND, + SOL, + SRM, + STMX, + SUSHI, + SXP, + THETA, + UNI, + USDC, + USDT, + VET, + WAVES, + XMR, + XRP, + XTZ, + YFI, + ZEC, + ZIL + } + + public enum WorkerType + { + PriceOneMinute, + PriceFiveMinutes, + PriceFifteenMinutes, + PriceThirtyMinutes, + PriceOneHour, + PriceFourHour, + PriceOneDay, + TopVolumeTicker, + PositionManager, + Spotlight, + Fee, + PositionFetcher, + TraderWatcher, + LeaderboardWorker, + Noobiesboard + } + + public enum WorkflowUsage + { + Trading, + Task + } + + public enum FlowType + { + RsiDivergence, + FeedTicker, + OpenPosition + } + + public enum FlowOutput + { + Signal, + Candles, + Position, + MoneyManagement + } + +} diff --git a/src/Managing.Common/Managing.Common.csproj b/src/Managing.Common/Managing.Common.csproj new file mode 100644 index 0000000..b8ca5ae --- /dev/null +++ b/src/Managing.Common/Managing.Common.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + diff --git a/src/Managing.Core/AsyncLazy.cs b/src/Managing.Core/AsyncLazy.cs new file mode 100644 index 0000000..dece47c --- /dev/null +++ b/src/Managing.Core/AsyncLazy.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; + +namespace Managing.Core +{ + /// + /// http://blogs.msdn.com/b/pfxteam/archive/2011/01/15/asynclazy-lt-t-gt.aspx + /// + /// + public class AsyncLazy : Lazy> + { + public AsyncLazy(Func valueFactory) : + base(() => Task.Factory.StartNew(valueFactory)) + { } + + public AsyncLazy(Func> taskFactory) : + base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap()) + { } + + public TaskAwaiter GetAwaiter() { return Value.GetAwaiter(); } + } + +} diff --git a/src/Managing.Core/DateHelpers.cs b/src/Managing.Core/DateHelpers.cs new file mode 100644 index 0000000..2d5f14f --- /dev/null +++ b/src/Managing.Core/DateHelpers.cs @@ -0,0 +1,10 @@ +namespace Managing.Core; + +public class DateHelpers +{ + public static DateTime GetFromUnixTimestamp(int unixTimestamp) + { + var dat_Time = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Local); + return dat_Time.AddSeconds(unixTimestamp); + } +} diff --git a/src/Managing.Core/Managing.Core.csproj b/src/Managing.Core/Managing.Core.csproj new file mode 100644 index 0000000..d0c5e78 --- /dev/null +++ b/src/Managing.Core/Managing.Core.csproj @@ -0,0 +1,20 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + + + + + + + + diff --git a/src/Managing.Core/MathHelpers.cs b/src/Managing.Core/MathHelpers.cs new file mode 100644 index 0000000..d6f4e29 --- /dev/null +++ b/src/Managing.Core/MathHelpers.cs @@ -0,0 +1,25 @@ +namespace Managing.Core +{ + public static class MathHelpers + { + public static int GetDecimalPlaces(decimal n) + { + n = Math.Abs(n); //make sure it is positive. + n -= (int)n; //remove the integer part of the number. + var decimalPlaces = 0; + + while (n > 0) + { + decimalPlaces++; + n *= 10; + n -= (int)n; + } + return decimalPlaces; + } + + public static int GetDecimalPlaces(string n) + { + return n.Split('.')[1].Length - 1; + } + } +} diff --git a/src/Managing.Core/Middleawares/GlobalErrorHandlingMiddleware.cs b/src/Managing.Core/Middleawares/GlobalErrorHandlingMiddleware.cs new file mode 100644 index 0000000..d8cd08a --- /dev/null +++ b/src/Managing.Core/Middleawares/GlobalErrorHandlingMiddleware.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Http; +using System.Net; +using System.Text.Json; + +namespace Managing.Api.WorkersExceptions; + +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); + } +} diff --git a/src/Managing.Core/MiscExtensions.cs b/src/Managing.Core/MiscExtensions.cs new file mode 100644 index 0000000..45bccf5 --- /dev/null +++ b/src/Managing.Core/MiscExtensions.cs @@ -0,0 +1,66 @@ +namespace Managing.Core +{ + public static class MiscExtensions + { + // Ex: collection.TakeLast(5); + //public static IEnumerable TakeLast(this IEnumerable source, int N) + //{ + // return source.Skip(Math.Max(0, source.Count() - N)); + //} + + // foreach(IEnumerable batch in users.Batch(1000)) + public static IEnumerable> Batch( + this IEnumerable source, int size) + { + T[] bucket = null; + var count = 0; + + foreach (var item in source) + { + if (bucket == null) + bucket = new T[size]; + + bucket[count++] = item; + + if (count != size) + continue; + + yield return bucket.Select(x => x); + + bucket = null; + count = 0; + } + + // Return the last bucket with all remaining elements + if (bucket != null && count > 0) + { + Array.Resize(ref bucket, count); + yield return bucket.Select(x => x); + } + } + + public static void AddItem(this List list, T item) + { + if (!list.Contains(item)) + { + list.Add(item); + } + } + + public static T ParseEnum(string value) + { + return (T)Enum.Parse(typeof(T), value, true); + } + + public static string Base64Encode(string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + public static string Base64Decode(string base64EncodedData) + { + var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); + return System.Text.Encoding.UTF8.GetString(base64EncodedBytes); + } + } +} diff --git a/src/Managing.Core/ValueObject.cs b/src/Managing.Core/ValueObject.cs new file mode 100644 index 0000000..57dac37 --- /dev/null +++ b/src/Managing.Core/ValueObject.cs @@ -0,0 +1,40 @@ +namespace Managing.Core +{ + public abstract class ValueObject + { + protected static bool EqualOperator(ValueObject left, ValueObject right) + { + if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null)) + { + return false; + } + return ReferenceEquals(left, null) || left.Equals(right); + } + + protected static bool NotEqualOperator(ValueObject left, ValueObject right) + { + return !(EqualOperator(left, right)); + } + + protected abstract IEnumerable GetEqualityComponents(); + + public override bool Equals(object obj) + { + if (obj == null || obj.GetType() != GetType()) + { + return false; + } + + var other = (ValueObject)obj; + + return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); + } + + public override int GetHashCode() + { + return GetEqualityComponents() + .Select(x => x != null ? x.GetHashCode() : 0) + .Aggregate((x, y) => x ^ y); + } + } +} diff --git a/src/Managing.Docker/.dockerignore b/src/Managing.Docker/.dockerignore new file mode 100644 index 0000000..c7ef99f --- /dev/null +++ b/src/Managing.Docker/.dockerignore @@ -0,0 +1,29 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +**/Managing.WebApp/**/* +**/Managing.WebApp/ +Managing.WebApp/**/* +Managing.WebApp/ +LICENSE +README.md \ No newline at end of file diff --git a/src/Managing.Docker/docker-compose.sandbox.yml b/src/Managing.Docker/docker-compose.sandbox.yml new file mode 100644 index 0000000..d4c7d4e --- /dev/null +++ b/src/Managing.Docker/docker-compose.sandbox.yml @@ -0,0 +1,67 @@ +version: '3.4' + + +services: + managing.api: + environment: + - ASPNETCORE_ENVIRONMENT=oda-docker + - ASPNETCORE_URLS=https://+:443;http://+:80 + - ASPNETCORE_Kestrel__Certificates__Default__Password=!MotdepasseFort11 + - ASPNETCORE_Kestrel__Certificates__Default__Path=/app/managing_cert.pfx + ports: + - "80:80" + - "443:443" + volumes: + - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro + - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro + depends_on: + - managingdb + + managing.api.workers: + environment: + - ASPNETCORE_ENVIRONMENT=oda-docker + - ASPNETCORE_URLS=https://+:443;http://+:80 + - ASPNETCORE_Kestrel__Certificates__Default__Password=!MotdepasseFort11 + - ASPNETCORE_Kestrel__Certificates__Default__Path=/app/managing_cert.pfx + ports: + - "81:80" + - "444:443" + volumes: + - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro + depends_on: + - managingdb + + managingdb: + restart: always + volumes: + - mongodata:/data/db + ports: + - "27017:27017" + + elasticsearch: + ports: + - 9200:9200 + volumes: + - elasticsearch-data:/usr/share/elasticsearch/data + environment: + - discovery.type=single-node + - xpack.monitoring.templates.enabled=true + - ES_JAVA_OPTS=-Xms1g -Xmx1g + - xpack.security.enabled=false + + kibana: + ports: + - 5601:5601 + depends_on: + - elasticsearch + environment: + - ELASTICSEARCH_URL=http://elasticsearch:9200 + + influxdb: + image: influxdb:latest + volumes: + - influxdata:/var/lib/influxdb2 + ports: + - 8086:8086 + restart: always + diff --git a/src/Managing.Docker/docker-compose.yml b/src/Managing.Docker/docker-compose.yml new file mode 100644 index 0000000..f86658c --- /dev/null +++ b/src/Managing.Docker/docker-compose.yml @@ -0,0 +1,46 @@ +version: '3.4' + +services: + managingdb: + image: mongo + networks: + - managing-network + + managing.api: + image: ${DOCKER_REGISTRY-}managingapi + build: + context: ../. + dockerfile: Managing.Api/Dockerfile + networks: + - managing-network + + managing.api.workers: + image: ${DOCKER_REGISTRY-}managingapiworkers + build: + context: ../. + dockerfile: Managing.Api.Workers/Dockerfile + networks: + - managing-network + + elasticsearch: + image: elasticsearch:8.4.1 + networks: + - managing-network + + kibana: + image: kibana:8.4.1 + + influxdb: + image: influxdb:latest + networks: + - managing-network + +volumes: + elasticsearch-data: + driver: local + mongodata: {} + influxdata: {} + +networks: + managing-network: + name: managing-network \ No newline at end of file diff --git a/src/Managing.Domain.Workers/Managing.Domain.Workers.csproj b/src/Managing.Domain.Workers/Managing.Domain.Workers.csproj new file mode 100644 index 0000000..132c02c --- /dev/null +++ b/src/Managing.Domain.Workers/Managing.Domain.Workers.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/src/Managing.Domain.Workers/Worker.cs b/src/Managing.Domain.Workers/Worker.cs new file mode 100644 index 0000000..fc4efe7 --- /dev/null +++ b/src/Managing.Domain.Workers/Worker.cs @@ -0,0 +1,7 @@ +namespace Managing.Domain.Workers +{ + public class Class1 + { + + } +} \ No newline at end of file diff --git a/src/Managing.Domain/Accounts/Account.cs b/src/Managing.Domain/Accounts/Account.cs new file mode 100644 index 0000000..1c5b042 --- /dev/null +++ b/src/Managing.Domain/Accounts/Account.cs @@ -0,0 +1,19 @@ +using Managing.Domain.Users; +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Accounts; + +public class Account +{ + [Required] + public string Name { get; set; } + [Required] + public TradingExchanges Exchange { get; set; } + [Required] + public AccountType Type { get; set; } + public string Key { get; set; } + public string Secret { get; set; } + public User User { get; set; } + public List Balances { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Domain/Accounts/Balance.cs b/src/Managing.Domain/Accounts/Balance.cs new file mode 100644 index 0000000..e066ee0 --- /dev/null +++ b/src/Managing.Domain/Accounts/Balance.cs @@ -0,0 +1,14 @@ +using Managing.Domain.Evm; + +namespace Managing.Domain.Accounts; + +public class Balance +{ + public string TokenImage { get; set; } + public string TokenName { get; set; } + public decimal Amount { get; set; } + public decimal Price { get; set; } + public decimal Value { get; set; } + public string TokenAdress { get; set; } + public Chain Chain { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Domain/Backtests/Backtest.cs b/src/Managing.Domain/Backtests/Backtest.cs new file mode 100644 index 0000000..f981ba5 --- /dev/null +++ b/src/Managing.Domain/Backtests/Backtest.cs @@ -0,0 +1,74 @@ +using Exilion.TradingAtomics; +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.Domain.Backtests; + +public class Backtest +{ + public Backtest( + Ticker ticker, + string scenario, + List positions, + List signals, + Timeframe timeframe, + List candles, + BotType botType, + string accountName) + { + Ticker = ticker; + Positions = positions; + Signals = signals; + Timeframe = timeframe; + Candles = candles; + Scenario = scenario; + BotType = botType; + AccountName = accountName; + } + + [Required] + public string Id { get; set; } + [Required] + public decimal FinalPnl { get; set; } + [Required] + public int WinRate { get; set; } + [Required] + public decimal GrowthPercentage { get; set; } + [Required] + public decimal HodlPercentage { get; set; } + [Required] + public Ticker Ticker { get; } + [Required] + public string Scenario { get; set; } + [Required] + public List Positions { get; } + [Required] + public List Signals { get; } + [Required] + public Timeframe Timeframe { get; } + [Required] + public BotType BotType { get; } + [Required] + public string AccountName { get; } + [Required] + public List Candles { get; } + [Required] + public PerformanceMetrics Statistics { get; set; } + [Required] + public decimal Fees { get; set; } + [Required] + public List> WalletBalances { get; set; } + [Required] + public MoneyManagement OptimizedMoneyManagement { get; set; } + [Required] + public MoneyManagement MoneyManagement { get; set; } + + public string GetStringReport() + { + return $"{Ticker} | {Timeframe} | Positions: {Positions.Count} | Winrate: {WinRate}% | Pnl: {FinalPnl:#.##}$ | %Pnl: {GrowthPercentage:#.##}% | %Hodl: {HodlPercentage:#.##}%"; + } +} \ No newline at end of file diff --git a/src/Managing.Domain/Bots/Bot.cs b/src/Managing.Domain/Bots/Bot.cs new file mode 100644 index 0000000..ca4af2a --- /dev/null +++ b/src/Managing.Domain/Bots/Bot.cs @@ -0,0 +1,75 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Bots +{ + /// + /// A bot define what code should be run. + /// To run a code you have to herit from this class and implement the Run() method + /// + public abstract class Bot : IBot + { + public int ExecutionCount; + public string Identifier { get; set; } + public string Name { get; set; } + public int Interval { get; set; } + public BotStatus Status { get; set; } + private CancellationTokenSource CancellationToken { get; set; } + + public Bot(string name) + { + Identifier = $"{name}-{DateTime.Now:yyyyMMdd-hhmm}-{Guid.NewGuid()}"; + Name = name; + Status = BotStatus.Down; + CancellationToken = new CancellationTokenSource(); + ExecutionCount = 0; + } + + public virtual void Start() + { + Status = BotStatus.Up; + } + + public async Task InitWorker(Func action) + { + await Task.Run(async () => + { + while (Status == BotStatus.Up) + { + try + { + await action(); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + ExecutionCount++; + await Task.Delay(Interval, CancellationToken.Token); + if (CancellationToken.IsCancellationRequested) + break; + } + }, CancellationToken.Token); + } + + public void Stop() + { + Status = BotStatus.Down; + //CancellationToken.Cancel(); + } + + public void Restart() + { + Status = BotStatus.Up; + } + + public string GetStatus() + { + return Status.ToString(); + } + + public string GetName() + { + return Name; + } + } +} \ No newline at end of file diff --git a/src/Managing.Domain/Bots/IBot.cs b/src/Managing.Domain/Bots/IBot.cs new file mode 100644 index 0000000..7572a3d --- /dev/null +++ b/src/Managing.Domain/Bots/IBot.cs @@ -0,0 +1,12 @@ +namespace Managing.Domain.Bots +{ + public interface IBot + { + string Name { get; set; } + void Start(); + void Stop(); + void Restart(); + string GetStatus(); + string GetName(); + } +} diff --git a/src/Managing.Domain/Candles/Candle.cs b/src/Managing.Domain/Candles/Candle.cs new file mode 100644 index 0000000..c859d95 --- /dev/null +++ b/src/Managing.Domain/Candles/Candle.cs @@ -0,0 +1,34 @@ +using Managing.Common; +using Skender.Stock.Indicators; +using System.ComponentModel.DataAnnotations; + +namespace Managing.Domain.Candles +{ + public class Candle : IQuote + { + [Required] + public Enums.TradingExchanges Exchange { get; set; } + [Required] + public string Ticker { get; set; } + [Required] + public DateTime OpenTime { get; set; } + [Required] + public DateTime Date { get; set; } + [Required] + public decimal Open { get; set; } + [Required] + public decimal Close { get; set; } + public decimal Volume { get; } + [Required] + public decimal High { get; set; } + [Required] + public decimal Low { get; set; } + public decimal BaseVolume { get; set; } + public decimal QuoteVolume { get; set; } + public int TradeCount { get; set; } + public decimal TakerBuyBaseVolume { get; set; } + public decimal TakerBuyQuoteVolume { get; set; } + [Required] + public Enums.Timeframe Timeframe { get; set; } + } +} diff --git a/src/Managing.Domain/Candles/CandleExtensions.cs b/src/Managing.Domain/Candles/CandleExtensions.cs new file mode 100644 index 0000000..f33227a --- /dev/null +++ b/src/Managing.Domain/Candles/CandleExtensions.cs @@ -0,0 +1,71 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Candles; + +public static class CandleExtensions +{ + + public static Candle SetupClosingCandle(this Candle candle) + { + + return candle; + } + + public static DateTime GetBotPreloadSinceFromTimeframe(Timeframe timeframe) + { + return timeframe switch + { + Timeframe.FiveMinutes => DateTime.UtcNow.AddDays(-1), + Timeframe.FifteenMinutes => DateTime.UtcNow.AddDays(-5), + Timeframe.ThirtyMinutes => DateTime.UtcNow.AddDays(-10), + Timeframe.OneHour => DateTime.UtcNow.AddDays(-30), + Timeframe.FourHour => DateTime.UtcNow.AddDays(-60), + Timeframe.OneDay => DateTime.UtcNow.AddDays(-360), + _ => DateTime.Now.AddHours(-15), + }; + } + + public static DateTime GetPreloadSinceFromTimeframe(Timeframe timeframe) + { + return DateTime.UtcNow.AddDays(GetMinimalDays(timeframe)); + } + + public static int GetIntervalFromTimeframe(Timeframe timeframe) + { + return timeframe switch + { + Timeframe.OneDay => 3600000, // 1h + Timeframe.FiveMinutes => 120000, // 2min + Timeframe.FifteenMinutes => 60000, // 1 min + Timeframe.OneHour => 900000, // 15min + _ => 300000, // 5min + }; + } + + public static int GetUnixInterval(this Timeframe timeframe) + { + return timeframe switch + { + Timeframe.OneDay => 86400, + Timeframe.FiveMinutes => 300, + Timeframe.FifteenMinutes => 900, + Timeframe.FourHour => 14400, + Timeframe.OneHour => 3600, + _ => throw new NotImplementedException() + }; + } + + public static double GetMinimalDays(Timeframe timeframe) + { + return timeframe switch + { + Timeframe.FiveMinutes => -1, + Timeframe.FifteenMinutes => -5, + Timeframe.ThirtyMinutes => -10, + Timeframe.OneHour => -30, + Timeframe.FourHour => -60, + Timeframe.OneDay => -360, + _ => throw new NotImplementedException() + }; + } +} diff --git a/src/Managing.Domain/Evm/Chain.cs b/src/Managing.Domain/Evm/Chain.cs new file mode 100644 index 0000000..4abe622 --- /dev/null +++ b/src/Managing.Domain/Evm/Chain.cs @@ -0,0 +1,8 @@ +namespace Managing.Domain.Evm; + +public class Chain +{ + public string Id { get; set; } + public string RpcUrl { get; set; } + public string Name { get; set; } +} diff --git a/src/Managing.Domain/Evm/EvmBalance.cs b/src/Managing.Domain/Evm/EvmBalance.cs new file mode 100644 index 0000000..d31ba18 --- /dev/null +++ b/src/Managing.Domain/Evm/EvmBalance.cs @@ -0,0 +1,12 @@ +namespace Managing.Domain.Evm; + +public class EvmBalance +{ + public string TokenImage { get; set; } + public string TokenName { get; set; } + public decimal Balance { get; set; } + public decimal Price { get; set; } + public decimal Value { get; set; } + public string TokenAddress { get; set; } + public Chain Chain { get; set; } +} diff --git a/src/Managing.Domain/Evm/Holder.cs b/src/Managing.Domain/Evm/Holder.cs new file mode 100644 index 0000000..4fdad02 --- /dev/null +++ b/src/Managing.Domain/Evm/Holder.cs @@ -0,0 +1,14 @@ +namespace Managing.Domain.Evm; + +public class Holder +{ + public Holder(string holderAddress) + { + HolderAddress = holderAddress; + Nfts = new List(); + } + + public string HolderAddress { get; set; } + public decimal Yield { get; set; } + public List Nfts { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Domain/Evm/HolderExtensions.cs b/src/Managing.Domain/Evm/HolderExtensions.cs new file mode 100644 index 0000000..32b358e --- /dev/null +++ b/src/Managing.Domain/Evm/HolderExtensions.cs @@ -0,0 +1,31 @@ +namespace Managing.Domain.Evm; + + +public static class HolderExtensions +{ + public static void AddNft(this Holder holder, Nft nft) + { + if (holder.Nfts.FirstOrDefault(n => n.TokenId == nft.TokenId) == null) + { + holder.Nfts.Add(nft); + } + } + + public static List GetYields(this List holders, decimal yieldAmount) + { + var nftHeldCount = 0; + foreach (var holder in holders) + { + nftHeldCount += holder.Nfts.Count; + } + + var yieldPerNft = yieldAmount / nftHeldCount; + + foreach (var holder in holders) + { + holder.Yield = holder.Nfts.Count * yieldPerNft; + } + + return holders; + } +} diff --git a/src/Managing.Domain/Evm/Nft.cs b/src/Managing.Domain/Evm/Nft.cs new file mode 100644 index 0000000..0484eae --- /dev/null +++ b/src/Managing.Domain/Evm/Nft.cs @@ -0,0 +1,17 @@ +using System.Numerics; + +namespace Managing.Domain.Evm; + +public class Nft +{ + public Nft(string contractAddress, BigInteger tokenId, DateTime blockDate) + { + ContractAddress = contractAddress; + TokenId = tokenId; + BlockDate = blockDate; + } + + public string ContractAddress { get; set; } + public BigInteger TokenId { get; set; } + public DateTime BlockDate { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Domain/Evm/Subgraph.cs b/src/Managing.Domain/Evm/Subgraph.cs new file mode 100644 index 0000000..bed0b49 --- /dev/null +++ b/src/Managing.Domain/Evm/Subgraph.cs @@ -0,0 +1,9 @@ +using Managing.Common; + +namespace Managing.Domain.Evm; + +public class Subgraph +{ + public Enums.SubgraphProvider SubgraphProvider { get; set; } + public string Url { get; set; } +} diff --git a/src/Managing.Domain/Managing.Domain.csproj b/src/Managing.Domain/Managing.Domain.csproj new file mode 100644 index 0000000..7b6d9e0 --- /dev/null +++ b/src/Managing.Domain/Managing.Domain.csproj @@ -0,0 +1,19 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + + + + + + + diff --git a/src/Managing.Domain/MoneyManagements/MoneyManagement.cs b/src/Managing.Domain/MoneyManagements/MoneyManagement.cs new file mode 100644 index 0000000..4f6428a --- /dev/null +++ b/src/Managing.Domain/MoneyManagements/MoneyManagement.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.MoneyManagements +{ + public class MoneyManagement + { + [Required] + public string Name { get; set; } + [Required] + public Timeframe Timeframe { get; set; } + [Required] + public decimal BalanceAtRisk { get; set; } + [Required] + public decimal StopLoss { get; set; } + [Required] + public decimal TakeProfit { get; set; } + [Required] + public decimal Leverage { get; set; } + + public void FormatPercentage() + { + StopLoss /= 100; + TakeProfit /= 100; + BalanceAtRisk /= 100; + } + } +} diff --git a/src/Managing.Domain/Scenarios/Scenario.cs b/src/Managing.Domain/Scenarios/Scenario.cs new file mode 100644 index 0000000..bd9cfee --- /dev/null +++ b/src/Managing.Domain/Scenarios/Scenario.cs @@ -0,0 +1,20 @@ +using Managing.Domain.Strategies; + +namespace Managing.Domain.Scenarios +{ + public class Scenario + { + public Scenario(string name) + { + Name = name; + Strategies = new List(); + } + + public string Name { get; set; } + public List Strategies { get; set; } + public void AddStrategy(Strategy strategy) + { + Strategies.Add(strategy); + } + } +} diff --git a/src/Managing.Domain/Scenarios/ScenarioHelpers.cs b/src/Managing.Domain/Scenarios/ScenarioHelpers.cs new file mode 100644 index 0000000..32aad83 --- /dev/null +++ b/src/Managing.Domain/Scenarios/ScenarioHelpers.cs @@ -0,0 +1,148 @@ +using Managing.Application.Strategies; +using Managing.Domain.Strategies; +using static Managing.Common.Enums; + +namespace Managing.Domain.Scenarios; + +public static class ScenarioHelpers +{ + public static IEnumerable GetStrategiesFromScenario(Scenario scenario) + { + var strategies = new List(); + foreach (var strategy in scenario.Strategies) + { + IStrategy result = strategy.Type switch + { + StrategyType.StDev => new StDevContext(strategy.Name, strategy.Timeframe, strategy.Period.Value), + StrategyType.RsiDivergence => new RSIDivergenceStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value), + StrategyType.RsiDivergenceConfirm => new RSIDivergenceConfirmStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value), + StrategyType.MacdCross => new MACDCrossStrategy(strategy.Name, strategy.Timeframe, strategy.FastPeriods.Value, strategy.SlowPeriods.Value, strategy.SignalPeriods.Value), + StrategyType.EmaCross => new EmaCrossStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value), + StrategyType.ThreeWhiteSoldiers => new ThreeWhiteSoldiersStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value), + StrategyType.SuperTrend => new SuperTrendStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value, strategy.Multiplier.Value), + StrategyType.ChandelierExit => new ChandelierExitStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value, strategy.Multiplier.Value), + StrategyType.EmaTrend => new EmaTrendStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value), + StrategyType.StochRsiTrend => new StochRsiTrendStrategy(strategy.Name, strategy.Timeframe, strategy.Period.Value, strategy.StochPeriods.Value, strategy.SignalPeriods.Value, strategy.SmoothPeriods.Value), + StrategyType.Stc => new STCStrategy(strategy.Name, strategy.Timeframe, strategy.CyclePeriods.Value, strategy.FastPeriods.Value, strategy.SlowPeriods.Value), + _ => throw new NotImplementedException(), + }; + + strategies.Add(result); + } + + return strategies; + } + + public static Strategy BuildStrategy( + StrategyType type, + 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) + { + var strategy = new Strategy(name, timeframe, type); + + switch (type) + { + case StrategyType.RsiDivergence: + case StrategyType.RsiDivergenceConfirm: + case StrategyType.EmaTrend: + case StrategyType.EmaCross: + case StrategyType.StDev: + if (!period.HasValue) + { + throw new Exception($"Missing period for {strategy.Type} strategy type"); + } + else + { + strategy.Period = period.Value; + } + break; + case StrategyType.MacdCross: + if (!fastPeriods.HasValue || !slowPeriods.HasValue || !signalPeriods.HasValue) + { + throw new Exception($"Missing fastPeriods or slowPeriods or signalPeriods, for {strategy.Type} strategy type"); + } + else + { + strategy.FastPeriods = fastPeriods; + strategy.SlowPeriods = slowPeriods; + strategy.SignalPeriods = signalPeriods; + } + break; + break; + case StrategyType.ThreeWhiteSoldiers: + break; + case StrategyType.SuperTrend: + case StrategyType.ChandelierExit: + if (!period.HasValue || !multiplier.HasValue) + { + throw new Exception($"Missing period or multiplier, for {strategy.Type} strategy type"); + } + else + { + strategy.Period = period; + strategy.Multiplier = multiplier; + } + break; + case StrategyType.StochRsiTrend: + if (!period.HasValue + || !stochPeriods.HasValue + || !signalPeriods.HasValue + || !smoothPeriods.HasValue) + { + throw new Exception($"Missing period, stochPeriods, signalPeriods, smoothPeriods for {strategy.Type} strategy type"); + } + else + { + strategy.Period = period; + strategy.StochPeriods = stochPeriods; + strategy.SignalPeriods = signalPeriods; + strategy.SmoothPeriods = smoothPeriods; + } + break; + case StrategyType.Stc: + if (!fastPeriods.HasValue || !slowPeriods.HasValue || !cyclePeriods.HasValue) + { + throw new Exception($"Missing fastPeriods or slowPeriods or cyclePeriods, for {strategy.Type} strategy type"); + } + else + { + strategy.FastPeriods = fastPeriods; + strategy.SlowPeriods = slowPeriods; + strategy.CyclePeriods = cyclePeriods; + } + break; + default: + break; + } + + return strategy; + } + + public static SignalType GetSignalType(StrategyType type) + { + return type switch + { + StrategyType.RsiDivergence => SignalType.Signal, + StrategyType.RsiDivergenceConfirm => SignalType.Signal, + StrategyType.MacdCross => SignalType.Signal, + StrategyType.EmaCross => SignalType.Signal, + StrategyType.ThreeWhiteSoldiers => SignalType.Signal, + StrategyType.SuperTrend => SignalType.Signal, + StrategyType.ChandelierExit => SignalType.Signal, + StrategyType.EmaTrend => SignalType.Trend, + StrategyType.Composite => SignalType.Signal, + StrategyType.StochRsiTrend => SignalType.Trend, + StrategyType.Stc => SignalType.Signal, + StrategyType.StDev => SignalType.Context, + _ => throw new NotImplementedException(), + }; + } +} diff --git a/src/Managing.Domain/Shared/Helpers/RiskHelpers.cs b/src/Managing.Domain/Shared/Helpers/RiskHelpers.cs new file mode 100644 index 0000000..53d839e --- /dev/null +++ b/src/Managing.Domain/Shared/Helpers/RiskHelpers.cs @@ -0,0 +1,52 @@ +using Managing.Domain.MoneyManagements; +using static Managing.Common.Enums; + +namespace Managing.Domain.Shared.Helpers +{ + public static class RiskHelpers + { + public static decimal GetStopLossPrice(TradeDirection direction, decimal price, MoneyManagement moneyManagement) + { + return direction == TradeDirection.Long ? + price -= price * moneyManagement.StopLoss : + price += price * moneyManagement.StopLoss; + } + + public static decimal GetBalanceAtRisk(decimal balance, MoneyManagement moneyManagement) + { + decimal amountToRisk = balance * moneyManagement.BalanceAtRisk; + + if (amountToRisk <= 1) + { + throw new Exception("Cannot open trade, not enough balance"); + } + + return amountToRisk; + } + + public static decimal GetTakeProfitPrice(TradeDirection direction, decimal price, MoneyManagement moneyManagement, int count = 1) + { + decimal percentage = moneyManagement.TakeProfit * count; + + price = direction == TradeDirection.Short ? price -= price * percentage : price += price * percentage; + return price; + } + + public static RiskLevel GetRiskFromConfidence(Confidence confidence) + { + switch (confidence) + { + case Confidence.Low: + return RiskLevel.Low; + case Confidence.Medium: + return RiskLevel.Medium; + case Confidence.High: + return RiskLevel.High; + case Confidence.None: + break; + } + + return RiskLevel.Low; + } + } +} diff --git a/src/Managing.Domain/Shared/Helpers/TradingBox.cs b/src/Managing.Domain/Shared/Helpers/TradingBox.cs new file mode 100644 index 0000000..643f25b --- /dev/null +++ b/src/Managing.Domain/Shared/Helpers/TradingBox.cs @@ -0,0 +1,161 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Domain.Shared.Helpers; + +public static class TradingBox +{ + public static Signal GetSignal(HashSet newCandles, HashSet strategies, HashSet previousSignal) + { + var signalOnCandles = new HashSet(); + foreach (var strategy in strategies) + { + strategy.UpdateCandles(newCandles); + var signals = strategy.Run(); + + if (signals == null || signals.Count == 0) continue; + + foreach (var signal in signals.Where(s => s.Date == newCandles.Last().Date)) + { + if (previousSignal.SingleOrDefault(s => s.Identifier == signal.Identifier) == null) + { + if (previousSignal.Count == 0 || previousSignal.Last().Date < signal.Date) + { + signalOnCandles.Add(signal); + } + } + } + } + + if (signalOnCandles.Count != strategies.Count) + return null; + + var data = newCandles.First(); + return ComputeSignals(strategies, signalOnCandles, MiscExtensions.ParseEnum(data.Ticker), data.Timeframe); + } + + public static Signal ComputeSignals(HashSet strategies, HashSet signalOnCandles, Ticker ticker, Timeframe timeframe) + { + Signal signal = null; + if (strategies.Count > 1) + { + var trendSignal = signalOnCandles.Where(s => s.SignalType == SignalType.Trend); + var signals = signalOnCandles.Where(s => s.SignalType == SignalType.Signal); + var contextStrategiesCount = strategies.Count(s => s.SignalType == SignalType.Context); + var validContext = true; + + if (contextStrategiesCount > 0 && + signalOnCandles.Count(s => s.SignalType == SignalType.Context) != contextStrategiesCount) + { + validContext = false; + } + + if (signals.All(s => s.Direction == TradeDirection.Long) && trendSignal.All(t => t.Direction == TradeDirection.Long) && validContext) + { + signal = new Signal( + ticker, + TradeDirection.Long, + Confidence.High, + signals.Last().Candle, + signals.Last().Date, + signals.Last().Exchange, + timeframe, + StrategyType.Composite, SignalType.Signal); + } + else if (signals.All(s => s.Direction == TradeDirection.Short) && trendSignal.All(t => t.Direction == TradeDirection.Short) && validContext) + { + signal = new Signal( + ticker, + TradeDirection.Short, + Confidence.High, + signals.Last().Candle, + signals.Last().Date, + signals.Last().Exchange, + timeframe, + StrategyType.Composite, SignalType.Signal); + } + } + else + { + // Only one strategy, we just add the single signal to the bot + signal = signalOnCandles.Single(); + } + + return signal; + } + + public static MoneyManagement GetBestMoneyManagement(List candles, List positions, MoneyManagement originMoneyManagement) + { + // Foreach positions, identitify the price when the position is open + // Then, foreach candles, get the maximum price before the next position + // Then, identify the lowest price before the maximum price + // Base on that, return the best StopLoss and TakeProfit to use and build a + var moneyManagement = new MoneyManagement(); + var stoplossPercentage = new List(); + var takeProfitsPercentage = new List(); + + if (positions.Count == 0) + return null; + + for (var i = 0; i < positions.Count; i++) + { + var position = positions[i]; + var nextPosition = i + 1 < positions.Count ? positions[i + 1] : null; + var (stopLoss, takeProfit) = GetBestSLTPForPosition(candles, position, nextPosition); + + stoplossPercentage.Add(stopLoss); + takeProfitsPercentage.Add(takeProfit); + } + + moneyManagement.StopLoss = stoplossPercentage.Average(); + moneyManagement.TakeProfit = takeProfitsPercentage.Average(); + moneyManagement.BalanceAtRisk = originMoneyManagement.BalanceAtRisk * 100; + moneyManagement.Timeframe = originMoneyManagement.Timeframe; + moneyManagement.Leverage = originMoneyManagement.Leverage; + moneyManagement.Name = "Optimized"; + return moneyManagement; + } + + public static (decimal Stoploss, decimal TakeProfit) GetBestSLTPForPosition(List candles, Position position, Position nextPosition) + { + var stopLoss = 0M; + var takeProfit = 0M; + var candlesBeforeNextPosition = candles.Where(c => c.Date >= position.Date && c.Date <= (nextPosition == null ? candles.Last().Date : nextPosition.Date)); + + if (position.OriginDirection == TradeDirection.Long) + { + var maxPrice = candlesBeforeNextPosition.Max(c => c.High); + var minPrice = candlesBeforeNextPosition.TakeWhile(c => c.High <= maxPrice).Min(c => c.Low); + stopLoss = GetPercentageFromEntry(position.Open.Price, minPrice); + takeProfit = GetPercentageFromEntry(position.Open.Price, maxPrice); + } + else if (position.OriginDirection == TradeDirection.Short) + { + var minPrice = candlesBeforeNextPosition.Min(c => c.Low); + var maxPrice = candlesBeforeNextPosition.TakeWhile(c => c.Low >= minPrice).Max(c => c.High); + stopLoss = GetPercentageFromEntry(position.Open.Price, maxPrice); + takeProfit = GetPercentageFromEntry(position.Open.Price, minPrice); + } + + return (stopLoss, takeProfit); + } + + private static decimal GetPercentageFromEntry(decimal entry, decimal price) + { + return Math.Abs(100 - ((100 * price) / entry)); + } + + public static ProfitAndLoss GetProfitAndLoss(Position position, decimal quantity, decimal price) + { + var orders = new List> + { + new Tuple(position.Open.Quantity, position.Open.Price), + new Tuple(-quantity, price) + }; + return new ProfitAndLoss(orders, position.OriginDirection); + } +} diff --git a/src/Managing.Domain/Shared/Helpers/TradingHelpers.cs b/src/Managing.Domain/Shared/Helpers/TradingHelpers.cs new file mode 100644 index 0000000..c89ba22 --- /dev/null +++ b/src/Managing.Domain/Shared/Helpers/TradingHelpers.cs @@ -0,0 +1,99 @@ +using Exilion.TradingAtomics; +using Managing.Domain.Accounts; +using Managing.Domain.Statistics; +using static Managing.Common.Enums; + +namespace Managing.Domain.Shared.Helpers; + +public static class TradingHelpers +{ + public static decimal GetHodlPercentage(Candles.Candle candle1, Candles.Candle candle2) + { + return candle2.Close * 100 / candle1.Close - 100; + } + + public static decimal GetGrowthFromInitalBalance(decimal balance, decimal finalPnl) + { + var growth = balance + finalPnl; + + return growth * 100 / balance - 100; + } + + public static PerformanceMetrics GetStatistics(Dictionary pnls) + { + var priceSeries = new TimePriceSeries(pnls); + + return priceSeries.CalculatePerformanceMetrics(); + } + + public static decimal GetFeeAmount(decimal fee, decimal amount) + { + return fee * amount; + } + + public static decimal GetFeeAmount(decimal fee, decimal amount, TradingExchanges exchange) + { + if (exchange.Equals(TradingExchanges.Evm)) + return fee; + + return GetFeeAmount(fee, amount); + } + + public static bool IsAGoodTrader(Trader trader) + { + return trader.Winrate > 80 + && trader.TradeCount > 8 + && trader.AverageWin > Math.Abs(trader.AverageLoss) * 3 + && trader.Pnl > 0; + } + + public static bool IsABadTrader(Trader trader) + { + return trader.Winrate < 30 + && trader.TradeCount > 8 + && trader.AverageWin * 3 < Math.Abs(trader.AverageLoss) + && trader.Pnl < 0; + } + + public static List FindBadTrader(this List traders) + { + var filteredTrader = new List(); + foreach (var trader in traders) + { + if (IsABadTrader(trader)) + { + filteredTrader.Add(trader); + } + } + + return filteredTrader; + } + + public static List FindGoodTrader(this List traders) + { + var filteredTrader = new List(); + foreach (var trader in traders) + { + if (IsAGoodTrader(trader)) + { + filteredTrader.Add(trader); + } + } + + return filteredTrader; + } + + public static List MapToTraders(this List accounts) + { + var traders = new List(); + foreach (var account in accounts) + { + traders.Add(new Trader + { + Address = account.Key + }); + } + + return traders; + } +} diff --git a/src/Managing.Domain/Shared/Rules/Check.cs b/src/Managing.Domain/Shared/Rules/Check.cs new file mode 100644 index 0000000..4d27607 --- /dev/null +++ b/src/Managing.Domain/Shared/Rules/Check.cs @@ -0,0 +1,13 @@ +namespace Managing.Domain.Shared.Rules +{ + public static class Check + { + public static void That(IValidationRule rule) + { + if (!rule.IsValid()) + { + throw new RuleException(rule.Message); + } + } + } +} diff --git a/src/Managing.Domain/Shared/Rules/IValidationRules.cs b/src/Managing.Domain/Shared/Rules/IValidationRules.cs new file mode 100644 index 0000000..2c7c4f4 --- /dev/null +++ b/src/Managing.Domain/Shared/Rules/IValidationRules.cs @@ -0,0 +1,9 @@ +namespace Managing.Domain.Shared.Rules +{ + public interface IValidationRule + { + bool IsValid(); + + string Message { get; } + } +} diff --git a/src/Managing.Domain/Shared/Rules/RuleException.cs b/src/Managing.Domain/Shared/Rules/RuleException.cs new file mode 100644 index 0000000..3e4bf5e --- /dev/null +++ b/src/Managing.Domain/Shared/Rules/RuleException.cs @@ -0,0 +1,9 @@ +namespace Managing.Domain.Shared.Rules +{ + public class RuleException : Exception + { + public RuleException(string message) : base(message) + { + } + } +} diff --git a/src/Managing.Domain/Statistics/Spotlight.cs b/src/Managing.Domain/Statistics/Spotlight.cs new file mode 100644 index 0000000..694d8e3 --- /dev/null +++ b/src/Managing.Domain/Statistics/Spotlight.cs @@ -0,0 +1,38 @@ +using Managing.Domain.Scenarios; +using Managing.Domain.Strategies; +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +public class SpotlightOverview +{ + [Required] + public List Spotlights { get; set; } + [Required] + public DateTime DateTime { get; set; } + public Guid Identifier { get; set; } + public int ScenarioCount { get; set; } +} + +public class Spotlight +{ + [Required] + public Scenario Scenario { get; set; } + [Required] + public List TickerSignals { get; set; } +} + +public class TickerSignal +{ + [Required] + public Ticker Ticker { get; set; } + [Required] + public List FiveMinutes { get; set; } + [Required] + public List FifteenMinutes { get; set; } + [Required] + public List OneHour { get; set; } + [Required] + public List FourHour { get; set; } + [Required] + public List OneDay { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Domain/Statistics/TopVolumeTicker.cs b/src/Managing.Domain/Statistics/TopVolumeTicker.cs new file mode 100644 index 0000000..df18f33 --- /dev/null +++ b/src/Managing.Domain/Statistics/TopVolumeTicker.cs @@ -0,0 +1,12 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Statistics; + +public class TopVolumeTicker +{ +public Ticker Ticker { get; set; } +public DateTime Date { get; set; } +public decimal Volume { get; set; } +public int Rank { get; set; } +public TradingExchanges Exchange { get; set; } +} diff --git a/src/Managing.Domain/Statistics/Trader.cs b/src/Managing.Domain/Statistics/Trader.cs new file mode 100644 index 0000000..7ad671c --- /dev/null +++ b/src/Managing.Domain/Statistics/Trader.cs @@ -0,0 +1,12 @@ +namespace Managing.Domain.Statistics; + +public class Trader +{ + public string Address { get; set; } + public int Winrate { get; set; } + public decimal Pnl { get; set; } + public int TradeCount { get; set; } + public decimal AverageWin { get; set; } + public decimal AverageLoss { get; set; } + public decimal Roi { get; set; } +} diff --git a/src/Managing.Domain/Strategies/Base/EmaBaseStrategy.cs b/src/Managing.Domain/Strategies/Base/EmaBaseStrategy.cs new file mode 100644 index 0000000..609472d --- /dev/null +++ b/src/Managing.Domain/Strategies/Base/EmaBaseStrategy.cs @@ -0,0 +1,39 @@ +using Managing.Common; +using Managing.Domain.Candles; +using Skender.Stock.Indicators; + +namespace Managing.Domain.Strategies.Base; + +public abstract class EmaBaseStrategy : Strategy +{ + protected EmaBaseStrategy(string name, Enums.Timeframe timeframe, Enums.StrategyType type) : base(name, timeframe, type) + { + } + + protected List MapEmaToCandle(List ema, IEnumerable candles) + { + var emaList = new List(); + foreach (var candle in candles) + { + var currentEma = ema.Find(candle.Date); + if (currentEma != null && currentEma.Ema.HasValue) + { + emaList.Add(new CandleEma() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + Ema = currentEma.Ema.Value, + }); + } + } + return emaList; + } + + public class CandleEma : Candle + { + public double Ema { get; set; } + } +} diff --git a/src/Managing.Domain/Strategies/ChandelierExitStrategy.cs b/src/Managing.Domain/Strategies/ChandelierExitStrategy.cs new file mode 100644 index 0000000..822e411 --- /dev/null +++ b/src/Managing.Domain/Strategies/ChandelierExitStrategy.cs @@ -0,0 +1,115 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies; + +public class ChandelierExitStrategy : Strategy +{ + public List Signals { get; set; } + public ChandelierExitStrategy(string name, Timeframe timeframe, int period, double multiplier) : base(name, timeframe, StrategyType.ChandelierExit) + { + Signals = new List(); + Period = period; + Multiplier = multiplier; + MinimumHistory = 1 + Period.Value; + } + + public override List Run() + { + if (Candles.Count <= MinimumHistory) + { + return null; + } + + try + { + GetSignals(ChandelierType.Long); + GetSignals(ChandelierType.Short); + + return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private void GetSignals(ChandelierType chandelierType) + { + var chandelier = Candles.GetChandelier(Period.Value, Multiplier.Value, chandelierType).Where(s => s.ChandelierExit.HasValue).ToList(); + var chandelierCandle = MapChandelierToCandle(chandelier, Candles.TakeLast(MinimumHistory)); + var previousCandle = chandelierCandle[0]; + + foreach (var currentCandle in chandelierCandle.Skip(1)) + { + // Short + if (currentCandle.Close < previousCandle.ChandelierExit && + previousCandle.Close > previousCandle.ChandelierExit && + currentCandle.Close < previousCandle.Open && + chandelierType == ChandelierType.Short) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium); + } + + // Long + if (currentCandle.Close > previousCandle.ChandelierExit && + previousCandle.Close < previousCandle.ChandelierExit && + currentCandle.Close > currentCandle.Open && + chandelierType == ChandelierType.Long) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium); + } + + previousCandle = currentCandle; + } + } + + private List MapChandelierToCandle(List superTrend, IEnumerable candles) + { + var superTrends = new List(); + foreach (var candle in candles) + { + var currentChandelier = superTrend.Find(candle.Date); + if (currentChandelier != null) + { + superTrends.Add(new CandleChandelier() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + ChandelierExit = (decimal)currentChandelier.ChandelierExit.Value, + }); + } + } + + return superTrends; + } + + private void AddSignal(CandleChandelier candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal( + MiscExtensions.ParseEnum(candleSignal.Ticker), + direction, + confidence, + candleSignal, + candleSignal.Date, + candleSignal.Exchange, + timeframe, + Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + + private class CandleChandelier : Candle + { + public decimal ChandelierExit { get; internal set; } + } +} diff --git a/src/Managing.Domain/Strategies/EmaCrossStrategy.cs b/src/Managing.Domain/Strategies/EmaCrossStrategy.cs new file mode 100644 index 0000000..f1b7ca0 --- /dev/null +++ b/src/Managing.Domain/Strategies/EmaCrossStrategy.cs @@ -0,0 +1,69 @@ +using Managing.Core; +using Managing.Domain.Shared.Rules; +using Managing.Domain.Strategies; +using Managing.Domain.Strategies.Base; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Application.Strategies; + +public class EmaCrossStrategy : EmaBaseStrategy +{ + public List Signals { get; set; } + + public EmaCrossStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.EmaCross) + { + Signals = new List(); + Period = period; + } + + public override List Run() + { + if (Candles.Count <= Period) + { + return null; + } + + try + { + var ema = Candles.GetEma(Period.Value).ToList(); + var emaCandles = MapEmaToCandle(ema, Candles.TakeLast(Period.Value)); + + if (ema.Count == 0) + return null; + + var previousCandle = emaCandles[0]; + foreach (var currentCandle in emaCandles.Skip(1)) + { + if (previousCandle.Close > (decimal)currentCandle.Ema && + currentCandle.Close < (decimal)currentCandle.Ema) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium); + } + + if (previousCandle.Close < (decimal)currentCandle.Ema && + currentCandle.Close > (decimal)currentCandle.Ema) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium); + } + + previousCandle = currentCandle; + } + + return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private void AddSignal(CandleEma candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal(MiscExtensions.ParseEnum(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } +} diff --git a/src/Managing.Domain/Strategies/EmaTrendStrategy.cs b/src/Managing.Domain/Strategies/EmaTrendStrategy.cs new file mode 100644 index 0000000..c9e6052 --- /dev/null +++ b/src/Managing.Domain/Strategies/EmaTrendStrategy.cs @@ -0,0 +1,65 @@ +using Managing.Core; +using Managing.Domain.Shared.Rules; +using Managing.Domain.Strategies.Base; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies; + +public class EmaTrendStrategy : EmaBaseStrategy +{ + public List Signals { get; set; } + + public EmaTrendStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.EmaTrend) + { + Signals = new List(); + Period = period; + } + + public override List Run() + { + if (Candles.Count <= 2 * Period) + { + return null; + } + + try + { + var ema = Candles.GetEma(Period.Value).ToList(); + var emaCandles = MapEmaToCandle(ema, Candles.TakeLast(Period.Value)); + + if (ema.Count == 0) + return null; + + var previousCandle = emaCandles[0]; + foreach (var currentCandle in emaCandles.Skip(1)) + { + if (currentCandle.Close > (decimal)currentCandle.Ema) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.None); + } + else + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.None); + } + + previousCandle = currentCandle; + } + + return Signals.OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + public void AddSignal(CandleEma candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal(MiscExtensions.ParseEnum(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } +} diff --git a/src/Managing.Domain/Strategies/IStrategy.cs b/src/Managing.Domain/Strategies/IStrategy.cs new file mode 100644 index 0000000..384fcdb --- /dev/null +++ b/src/Managing.Domain/Strategies/IStrategy.cs @@ -0,0 +1,20 @@ +using Managing.Domain.Candles; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies +{ + public interface IStrategy + { + string Name { get; set; } + StrategyType Type { get; set; } + SignalType SignalType { get; set; } + int? Period { get; set; } + int? FastPeriods { get; set; } + int? SlowPeriods { get; set; } + int? SignalPeriods { get; set; } + + List Run(); + void UpdateCandles(HashSet newCandles); + string GetName(); + } +} diff --git a/src/Managing.Domain/Strategies/MACDCrossStrategy.cs b/src/Managing.Domain/Strategies/MACDCrossStrategy.cs new file mode 100644 index 0000000..1bdbe56 --- /dev/null +++ b/src/Managing.Domain/Strategies/MACDCrossStrategy.cs @@ -0,0 +1,102 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies; + +public class MACDCrossStrategy : Strategy +{ + public List Signals { get; set; } + + public MACDCrossStrategy(string name, Timeframe timeframe, int fastPeriods, int slowPeriods, int signalPeriods) : base(name, timeframe, StrategyType.MacdCross) + { + Signals = new List(); + FastPeriods = fastPeriods; + SlowPeriods = slowPeriods; + SignalPeriods = signalPeriods; + } + + public override List Run() + { + if (Candles.Count <= 2*(SlowPeriods + SignalPeriods)) + { + return null; + } + + try + { + var macd = Candles.GetMacd(FastPeriods.Value, SlowPeriods.Value, SignalPeriods.Value).ToList(); + var macdCandle = MapMacdToCandle(macd, Candles.TakeLast(SignalPeriods.Value)); + + if (macd.Count == 0) + return null; + + var previousCandle = macdCandle[0]; + foreach (var currentCandle in macdCandle.Skip(1)) + { + if (previousCandle.Histogram > 0 && currentCandle.Histogram < 0 && currentCandle.Macd < 0) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium); + } + + if (previousCandle.Histogram < 0 && currentCandle.Histogram > 0 && currentCandle.Macd > 0) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium); + } + + previousCandle = currentCandle; + } + + return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private List MapMacdToCandle(List macd, IEnumerable candles) + { + var macdList = new List(); + foreach (var candle in candles) + { + var currentMacd = macd.Find(candle.Date); + if (currentMacd != null) + { + macdList.Add(new CandleMacd() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + FastEma = currentMacd.FastEma.Value, + SlowEma = currentMacd.SlowEma.Value, + Macd = currentMacd.Macd.Value, + Histogram = currentMacd.Histogram.Value + }); + } + } + return macdList; + } + + private void AddSignal(CandleMacd candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal(MiscExtensions.ParseEnum(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + private class CandleMacd : Candle + { + public double Macd { get; set; } + public double Signal { get; set; } + public double Histogram { get; set; } + public double FastEma { get; set; } + public double SlowEma { get; set; } + } +} diff --git a/src/Managing.Domain/Strategies/RSIDivergenceConfirmStrategy.cs b/src/Managing.Domain/Strategies/RSIDivergenceConfirmStrategy.cs new file mode 100644 index 0000000..523abb8 --- /dev/null +++ b/src/Managing.Domain/Strategies/RSIDivergenceConfirmStrategy.cs @@ -0,0 +1,252 @@ +using Managing.Core; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; +using Candle = Managing.Domain.Candles.Candle; + +namespace Managing.Domain.Strategies; + +public class RSIDivergenceConfirmStrategy : Strategy +{ + public List Signals { get; set; } + + public RSIDivergenceConfirmStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.RsiDivergenceConfirm) + { + Period = period; + Signals = new List(); + } + + /// + /// Get RSI signals + /// + /// + public override List Run() + { + if (Candles.Count <= Period) + { + return null; + } + + var ticker = Candles.First().Ticker; + + try + { + var rsiResult = Candles.TakeLast(10 * Period.Value).GetRsi(Period.Value).ToList(); + var candlesRsi = MapRsiToCandle(rsiResult, Candles.TakeLast(10 * Period.Value)); + + if (candlesRsi.Count(c => c.Rsi > 0) == 0) + return null; + + GetLongSignals(candlesRsi); + GetShortSignals(candlesRsi); + + return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private void GetLongSignals(List candlesRsi) + { + // Set the low and high for first candle + var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0); + var highPrices = new List(); + var lowPrices = new List(); + + var highRsi = new List(); + var lowRsi = new List(); + + highPrices.Add(firstCandleRsi); + lowPrices.Add(firstCandleRsi); + + highRsi.Add(firstCandleRsi); + lowRsi.Add(firstCandleRsi); + + var previousCandle = firstCandleRsi; + + // For a long + foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1)) + { + // If price go down + if (previousCandle.Close > currentCandle.Close) + { + // because the last price is upper than the current + highPrices.AddItem(previousCandle); + + // Check if rsi is higher than the last lowest + if (currentCandle.Rsi > lowRsi.TakeLast(Period.Value).Min(r => r.Rsi)) + { + // If new higher high, we set it + if (currentCandle.Rsi > highRsi.Last().Rsi) + highRsi.AddItem(currentCandle); + + if (currentCandle.Rsi > lowRsi.Last().Rsi) + lowRsi.AddItem(currentCandle); + + // Price go down but RSI go up + if (currentCandle.Close < lowPrices.TakeLast(Period.Value).Min(p => p.Close)) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.None); + } + } + else + { + // No divergence, price go down, rsi go down + lowRsi.AddItem(currentCandle); + } + + lowPrices.AddItem(currentCandle); + } + else + { + // Price go up, so we have to update if price is a new higher high than previous candle + // Normally always true + if (previousCandle.Close < currentCandle.Close) + highPrices.AddItem(currentCandle); //15-15-12-14-17 + + // If rsi is lower low or not set + if (currentCandle.Rsi < lowRsi.Last().Rsi || lowRsi.Last().Rsi == 0) + lowRsi.AddItem(currentCandle); + + // Price going up, so if its a new high we set it + if (currentCandle.Rsi > highRsi.Last().Rsi) + highRsi.AddItem(currentCandle); + } + + CheckIfConfimation(currentCandle, TradeDirection.Long); + + previousCandle = currentCandle; + } + } + + private void GetShortSignals(List candlesRsi) + { + // Set the low and high for first candle + var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0); + + var signals = new List(); + var highPrices = new List(); + var lowPrices = new List(); + + var highRsi = new List(); + var lowRsi = new List(); + + highPrices.Add(firstCandleRsi); + lowPrices.Add(firstCandleRsi); + + highRsi.Add(firstCandleRsi); + lowRsi.Add(firstCandleRsi); + + var previousCandle = firstCandleRsi; + + // For a short + foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1)) + { + // If price go up + if (previousCandle.Close < currentCandle.Close) + { + // because the last price is lower than the current + lowPrices.AddItem(previousCandle); + + // Check if rsi is lower than the last high + if (currentCandle.Rsi < highRsi.TakeLast(Period.Value).Max(r => r.Rsi)) + { + // If new lower low, we set it + if (currentCandle.Rsi < lowRsi.Last().Rsi) + lowRsi.AddItem(currentCandle); + + if (currentCandle.Rsi < highRsi.Last().Rsi) + highRsi.AddItem(currentCandle); + + // Price go up but RSI go down + if (currentCandle.Close > highPrices.TakeLast(Period.Value).Max(p => p.Close)) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.None); + } + } + else + { + // No divergence, price go up, rsi go up + highRsi.AddItem(currentCandle); + } + + highPrices.AddItem(currentCandle); + } + else + { + // Price go down, so we have to update if price is a new lower low than previous candle + if (previousCandle.Close > currentCandle.Close) + lowPrices.AddItem(currentCandle); + + // If rsi is higher high or not set + if (currentCandle.Rsi > highRsi.Last().Rsi || highRsi.Last().Rsi == 0) + highRsi.AddItem(currentCandle); + + // Price going down, so if its a new low we set it + if (currentCandle.Rsi < lowRsi.Last().Rsi) + lowRsi.AddItem(currentCandle); + } + + CheckIfConfimation(currentCandle, TradeDirection.Short); + + previousCandle = currentCandle; + } + } + + private void CheckIfConfimation(CandleRsi currentCandle, TradeDirection direction) + { + var lastCandleOnPeriod = Candles.TakeLast(Period.Value).ToList(); + var signalsOnPeriod = Signals.Where(s => s.Date >= lastCandleOnPeriod[0].Date + && s.Date < currentCandle.Date + && s.Direction == direction + && s.Confidence == Confidence.None + && s.Status != SignalStatus.Expired + && s.Status != SignalStatus.PositionOpen).ToList(); + + foreach (var signal in signalsOnPeriod) + { + if (direction == TradeDirection.Short && currentCandle.Close < signal.Candle.Open) + { + AddSignal(currentCandle, Timeframe, direction, Confidence.High); + Signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; + } + + if (direction == TradeDirection.Long && currentCandle.Close > signal.Candle.Open) + { + AddSignal(currentCandle, Timeframe, direction, Confidence.High); + Signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; + } + } + } + + private void AddSignal(CandleRsi candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal(MiscExtensions.ParseEnum(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + private List MapRsiToCandle(IReadOnlyCollection rsiResult, + IEnumerable candles) + { + + return candles.Select(c => new CandleRsi() + { + Close = c.Close, + Open = c.Open, + Rsi = rsiResult.Find(c.Date).Rsi.GetValueOrDefault(), + Date = c.Date, + Ticker = c.Ticker, + Exchange = c.Exchange + }).ToList(); + } + + private class CandleRsi : Candle + { + public double Rsi { get; set; } + } +} diff --git a/src/Managing.Domain/Strategies/RSIDivergenceStrategy.cs b/src/Managing.Domain/Strategies/RSIDivergenceStrategy.cs new file mode 100644 index 0000000..4644d1b --- /dev/null +++ b/src/Managing.Domain/Strategies/RSIDivergenceStrategy.cs @@ -0,0 +1,234 @@ +using Managing.Core; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; +using Candle = Managing.Domain.Candles.Candle; + +namespace Managing.Domain.Strategies; + +public class RSIDivergenceStrategy : Strategy +{ + public List Signals { get; set; } + public TradeDirection Direction { get; set; } + private const int UpperBand = 70; + private const int LowerBand = 30; + + public RSIDivergenceStrategy(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.RsiDivergence) + { + Period = period; + Signals = new List(); + } + + /// + /// Get RSI signals + /// + /// + public override List Run() + { + if (!Period.HasValue || Candles.Count <= Period) + { + return null; + } + + var ticker = Candles.First().Ticker; + + try + { + var rsiResult = Candles.TakeLast(10 * Period.Value).GetRsi(Period.Value).ToList(); + var candlesRsi = MapRsiToCandle(rsiResult, Candles.TakeLast(10 * Period.Value)); + + if (candlesRsi.Count(c => c.Rsi > 0) == 0) + return null; + + GetLongSignals(candlesRsi); + GetShortSignals(candlesRsi); + + return Signals; + } + catch (RuleException) + { + return null; + } + } + + private void GetLongSignals(List candlesRsi) + { + // Set the low and high for first candle + var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0); + var highPrices = new List(); + var lowPrices = new List(); + + var highRsi = new List(); + var lowRsi = new List(); + + highPrices.Add(firstCandleRsi); + lowPrices.Add(firstCandleRsi); + + highRsi.Add(firstCandleRsi); + lowRsi.Add(firstCandleRsi); + + var previousCandle = firstCandleRsi; + + // For a long + foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1)) + { + // If price go down + if (previousCandle.Close > currentCandle.Close) + { + // because the last price is upper than the current + highPrices.AddItem(previousCandle); + + // Check if rsi is higher than the last lowest + if (currentCandle.Rsi > lowRsi.TakeLast(Period.Value).Min(r => r.Rsi)) + { + // If new higher high, we set it + if (currentCandle.Rsi > highRsi.Last().Rsi) + highRsi.AddItem(currentCandle); + + if (currentCandle.Rsi > lowRsi.Last().Rsi) + lowRsi.AddItem(currentCandle); + + // Price go down but RSI go up + if (currentCandle.Close < lowPrices.TakeLast(Period.Value).Min(p => p.Close)) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long); + } + } + else + { + // No divergence, price go down, rsi go down + lowRsi.AddItem(currentCandle); + } + + lowPrices.AddItem(currentCandle); + } + else + { + // Price go up, so we have to update if price is a new higher high than previous candle + // Normally always true + if (previousCandle.Close < currentCandle.Close) + highPrices.AddItem(currentCandle); //15-15-12-14-17 + + // If rsi is lower low or not set + if (currentCandle.Rsi < lowRsi.Last().Rsi || lowRsi.Last().Rsi == 0) + lowRsi.AddItem(currentCandle); + + // Price going up, so if its a new high we set it + if (currentCandle.Rsi > highRsi.Last().Rsi) + highRsi.AddItem(currentCandle); + } + + previousCandle = currentCandle; + } + } + + private void GetShortSignals(List candlesRsi) + { + // Set the low and high for first candle + var firstCandleRsi = candlesRsi.First(c => c.Rsi > 0); + + var signals = new List(); + var highPrices = new List(); + var lowPrices = new List(); + + var highRsi = new List(); + var lowRsi = new List(); + + highPrices.Add(firstCandleRsi); + lowPrices.Add(firstCandleRsi); + + highRsi.Add(firstCandleRsi); + lowRsi.Add(firstCandleRsi); + + var previousCandle = firstCandleRsi; + + // For a short + foreach (var currentCandle in candlesRsi.FindAll(r => r.Rsi > 0).Skip(1)) + { + // If price go up + if (previousCandle.Close < currentCandle.Close) + { + // because the last price is lower than the current + lowPrices.AddItem(previousCandle); + + // Check if rsi is lower than the last high + if (currentCandle.Rsi < highRsi.TakeLast(Period.Value).Max(r => r.Rsi)) + { + // If new lower low, we set it + if (currentCandle.Rsi < lowRsi.Last().Rsi) + lowRsi.AddItem(currentCandle); + + if (currentCandle.Rsi < highRsi.Last().Rsi) + highRsi.AddItem(currentCandle); + + // Price go up but RSI go down + if (currentCandle.Close > highPrices.TakeLast(Period.Value).Max(p => p.Close)) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short); + } + } + else + { + // No divergence, price go up, rsi go up + highRsi.AddItem(currentCandle); + } + + highPrices.AddItem(currentCandle); + } + else + { + // Price go down, so we have to update if price is a new lower low than previous candle + if (previousCandle.Close > currentCandle.Close) + lowPrices.AddItem(currentCandle); + + // If rsi is higher high or not set + if (currentCandle.Rsi > highRsi.Last().Rsi || highRsi.Last().Rsi == 0) + highRsi.AddItem(currentCandle); + + // Price going down, so if its a new low we set it + if (currentCandle.Rsi < lowRsi.Last().Rsi) + lowRsi.AddItem(currentCandle); + } + + previousCandle = currentCandle; + } + } + + private void AddSignal(CandleRsi candleSignal, Timeframe timeframe, TradeDirection direction) + { + var signal = new Signal(MiscExtensions.ParseEnum(candleSignal.Ticker), direction, Confidence.Low, candleSignal, candleSignal.Date, candleSignal.Exchange, timeframe, Type, SignalType); + + if (Signals.Count(s => s.Identifier == signal.Identifier) < 1) + { + var lastCandleOnPeriod = Candles.TakeLast(Period.Value).ToList(); + var signalsOnPeriod = Signals.Where(s => s.Date >= lastCandleOnPeriod[0].Date).ToList(); + + if (signalsOnPeriod.Count == 1) + signal.SetConfidence(Confidence.Medium); + + if (signalsOnPeriod.Count >= 2) + signal.SetConfidence(Confidence.High); + + Signals.AddItem(signal); + } + } + + private List MapRsiToCandle(IReadOnlyCollection rsiResult, + IEnumerable candles) + { + + return candles.Select(c => new CandleRsi() + { + Close = c.Close, + Rsi = rsiResult.Find(c.Date).Rsi.GetValueOrDefault(), + Date = c.Date, + Ticker = c.Ticker, + Exchange = c.Exchange + }).ToList(); + } + + private class CandleRsi : Candle + { + public double Rsi { get; set; } + } +} diff --git a/src/Managing.Domain/Strategies/Rules/CloseHigherThanThePreviousHigh.cs b/src/Managing.Domain/Strategies/Rules/CloseHigherThanThePreviousHigh.cs new file mode 100644 index 0000000..c6f11ae --- /dev/null +++ b/src/Managing.Domain/Strategies/Rules/CloseHigherThanThePreviousHigh.cs @@ -0,0 +1,24 @@ +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; + +namespace Managing.Domain.Strategies.Rules +{ + public class CloseHigherThanThePreviousHigh : IValidationRule + { + private readonly Candle _previousCandles; + private readonly Candle _currentCandle; + + public CloseHigherThanThePreviousHigh(Candle previousCandles, Candle currentCandle) + { + _previousCandles = previousCandles; + _currentCandle = currentCandle; + } + + public string Message => $"Current candle did close higher than the previous high close candle"; + + public bool IsValid() + { + return _previousCandles != null ? _currentCandle.Close > _previousCandles.High : false; + } + } +} diff --git a/src/Managing.Domain/Strategies/Rules/CloseLowerThanThePreviousHigh.cs b/src/Managing.Domain/Strategies/Rules/CloseLowerThanThePreviousHigh.cs new file mode 100644 index 0000000..d020b7d --- /dev/null +++ b/src/Managing.Domain/Strategies/Rules/CloseLowerThanThePreviousHigh.cs @@ -0,0 +1,24 @@ +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; + +namespace Managing.Domain.Strategies.Rules +{ + public class CloseLowerThanThePreviousHigh : IValidationRule + { + private readonly Candle _previousCandles; + private readonly Candle _currentCandle; + + public CloseLowerThanThePreviousHigh(Candle previousCandles, Candle currentCandle) + { + _previousCandles = previousCandles; + _currentCandle = currentCandle; + } + + public string Message => $"Current candle did close lower than the previous high close candle"; + + public bool IsValid() + { + return _previousCandles != null ? _currentCandle.Close > _previousCandles.Low : false; + } + } +} \ No newline at end of file diff --git a/src/Managing.Domain/Strategies/Rules/RSIShouldBeBullish.cs b/src/Managing.Domain/Strategies/Rules/RSIShouldBeBullish.cs new file mode 100644 index 0000000..963ce86 --- /dev/null +++ b/src/Managing.Domain/Strategies/Rules/RSIShouldBeBullish.cs @@ -0,0 +1,21 @@ +using Managing.Domain.Shared.Rules; + +namespace Managing.Domain.Strategies.Rules +{ + public class RSIShouldBeBullish : IValidationRule + { + private int _rsiValue; + + public RSIShouldBeBullish(int rsiValue) + { + _rsiValue = rsiValue; + } + + public string Message => $"RSI is not bullish, current value : {_rsiValue}"; + + public bool IsValid() + { + return _rsiValue > 70; + } + } +} diff --git a/src/Managing.Domain/Strategies/STCStrategy.cs b/src/Managing.Domain/Strategies/STCStrategy.cs new file mode 100644 index 0000000..348e6a6 --- /dev/null +++ b/src/Managing.Domain/Strategies/STCStrategy.cs @@ -0,0 +1,103 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies; + +public class STCStrategy : Strategy +{ + public List Signals { get; set; } + + public STCStrategy(string name, Timeframe timeframe, int cyclePeriods, int fastPeriods, int slowPeriods) : base(name, timeframe, StrategyType.Stc) + { + Signals = new List(); + FastPeriods = fastPeriods; + SlowPeriods = slowPeriods; + CyclePeriods = cyclePeriods; + } + + public override List Run() + { + if (Candles.Count <= 2 * (SlowPeriods + CyclePeriods)) + { + return null; + } + + try + { + var stc = Candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList(); + var stcCandles = MapStcToCandle(stc, Candles.TakeLast(CyclePeriods.Value)); + + if (stc.Count == 0) + return null; + + var previousCandle = stcCandles[0]; + foreach (var currentCandle in stcCandles.Skip(1)) + { + if (previousCandle.Stc > 75 && currentCandle.Stc <= 75) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium); + } + + if (previousCandle.Stc < 25 && currentCandle.Stc >= 25) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium); + } + + previousCandle = currentCandle; + } + + return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private List MapStcToCandle(List stc, IEnumerable candles) + { + var sctList = new List(); + foreach (var candle in candles) + { + var currentSct = stc.Find(candle.Date); + if (currentSct != null) + { + sctList.Add(new CandleSct() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + Stc = currentSct.Stc + }); + } + } + return sctList; + } + + private void AddSignal(CandleSct candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal( + MiscExtensions.ParseEnum(candleSignal.Ticker), + direction, + confidence, + candleSignal, + candleSignal.Date, + candleSignal.Exchange, + timeframe, + Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + private class CandleSct : Candle + { + public double? Stc { get; internal set; } + } +} diff --git a/src/Managing.Domain/Strategies/Signal.cs b/src/Managing.Domain/Strategies/Signal.cs new file mode 100644 index 0000000..8414cb9 --- /dev/null +++ b/src/Managing.Domain/Strategies/Signal.cs @@ -0,0 +1,62 @@ +using Managing.Core; +using Managing.Domain.Candles; +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies +{ + public class Signal : ValueObject + { + [Required] + public SignalStatus Status { get; set; } + [Required] + public TradeDirection Direction { get; } + [Required] + public Confidence Confidence { get; private set; } + [Required] + public Timeframe Timeframe { get; } + [Required] + public DateTime Date { get; private set; } + [Required] + public Candle Candle { get; } + [Required] + public string Identifier { get; } + [Required] + public Ticker Ticker { get; } + [Required] + public TradingExchanges Exchange { get; set; } + [Required] + public StrategyType StrategyType { get; set; } + [Required] + public SignalType SignalType { get; set; } + + public Signal(Ticker ticker, TradeDirection direction, Confidence confidence, Candle candle, DateTime date, + TradingExchanges exchange, Timeframe timeframe, StrategyType strategyType, SignalType signalType) + { + Direction = direction; + Confidence = confidence; + Candle = candle; + Date = date; + Ticker = ticker; + Exchange = exchange; + Status = SignalStatus.WaitingForPosition; + Timeframe = timeframe; + StrategyType = strategyType; + + Identifier = $"{StrategyType}-{direction}-{ticker}-{Timeframe}-{candle?.Close}-{date:yyyyMMdd-HHmmss}"; + SignalType = signalType; + } + + public void SetConfidence(Confidence confidence) + { + Confidence = confidence; + } + + protected override IEnumerable GetEqualityComponents() + { + yield return Direction; + yield return Confidence; + yield return Date; + } + } +} diff --git a/src/Managing.Domain/Strategies/StDevContext.cs b/src/Managing.Domain/Strategies/StDevContext.cs new file mode 100644 index 0000000..6ea8af7 --- /dev/null +++ b/src/Managing.Domain/Strategies/StDevContext.cs @@ -0,0 +1,102 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies; + +public class StDevContext : Strategy +{ + public List Signals { get; set; } + + public StDevContext(string name, Timeframe timeframe, int period) : base(name, timeframe, StrategyType.StDev) + { + Signals = new List(); + Period = period; + } + + public override List Run() + { + if (Candles.Count <= Period) + { + return null; + } + + try + { + var stDev = Candles.GetStdDev(Period.Value).ToList(); + var stDevCandles = MapStDev(stDev, Candles.TakeLast(Period.Value)); + + if (stDev.Count == 0) + return null; + + var lastCandle = stDevCandles.Last(); + + if (lastCandle.ZScore is < 1.2 and > (-1.2)) + { + AddSignal(lastCandle, Timeframe, TradeDirection.None, Confidence.Medium); + } + else + { + Console.WriteLine("Bad zscore"); + } + + return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private List MapStDev(List stDev, IEnumerable candles) + { + var sctList = new List(); + foreach (var candle in candles) + { + var currentSct = stDev.Find(candle.Date); + if (currentSct != null) + { + sctList.Add(new CandleStDev() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + StDev = currentSct.StdDev, + ZScore = currentSct.ZScore, + StdDevSma = currentSct.StdDevSma, + Mean = currentSct.Mean + }); + } + } + return sctList; + } + + private void AddSignal(CandleStDev candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal( + MiscExtensions.ParseEnum(candleSignal.Ticker), + direction, + confidence, + candleSignal, + candleSignal.Date, + candleSignal.Exchange, + timeframe, + Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + private class CandleStDev : Candle + { + public double? StDev { get; internal set; } + public double? ZScore { get; internal set; } + public double? StdDevSma { get; internal set; } + public double? Mean { get; internal set; } + } +} diff --git a/src/Managing.Domain/Strategies/StochRsiTrendStrategy.cs b/src/Managing.Domain/Strategies/StochRsiTrendStrategy.cs new file mode 100644 index 0000000..bc591fe --- /dev/null +++ b/src/Managing.Domain/Strategies/StochRsiTrendStrategy.cs @@ -0,0 +1,112 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies; + +public class StochRsiTrendStrategy : Strategy +{ + public List Signals { get; set; } + + public StochRsiTrendStrategy( + string name, + Timeframe timeframe, + int period, + int stochPeriod, + int signalPeriod, + int smoothPeriods) : base(name, timeframe, StrategyType.StochRsiTrend) + { + Signals = new List(); + StochPeriods = stochPeriod; + SignalPeriods = signalPeriod; + SmoothPeriods = smoothPeriods; + Period = period; + } + + public override List Run() + { + if (Candles.Count <= 10 * Period + 50) + { + return null; + } + + try + { + var stochRsi = Candles.GetStochRsi(Period.Value, StochPeriods.Value, SignalPeriods.Value, SmoothPeriods.Value).RemoveWarmupPeriods().ToList(); + var stochRsiCandles = MapStochRsiToCandle(stochRsi, Candles.TakeLast(Period.Value)); + + if (stochRsi.Count == 0) + return null; + + var previousCandle = stochRsiCandles[0]; + foreach (var currentCandle in stochRsiCandles.Skip(1)) + { + if (currentCandle.Signal < 20) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.None); + } + else if (currentCandle.Signal > 80) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.None); + } + + previousCandle = currentCandle; + } + + return Signals.OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private List MapStochRsiToCandle(List ema, IEnumerable candles) + { + var emaList = new List(); + foreach (var candle in candles) + { + var currentEma = ema.Find(candle.Date); + if (currentEma != null) + { + emaList.Add(new CandleStochRsi() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + Signal = currentEma.Signal.Value, + StochRsi = currentEma.StochRsi.Value + }); + } + } + return emaList; + } + + private void AddSignal(CandleStochRsi candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal( + MiscExtensions.ParseEnum(candleSignal.Ticker), + direction, + confidence, + candleSignal, + candleSignal.Date, + candleSignal.Exchange, + timeframe, + Type, + SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + private class CandleStochRsi : Candle + { + public double Signal { get; internal set; } + public double StochRsi { get; internal set; } + } +} diff --git a/src/Managing.Domain/Strategies/Strategy.cs b/src/Managing.Domain/Strategies/Strategy.cs new file mode 100644 index 0000000..ec843a3 --- /dev/null +++ b/src/Managing.Domain/Strategies/Strategy.cs @@ -0,0 +1,58 @@ +using Managing.Domain.Candles; +using Managing.Core; +using static Managing.Common.Enums; +using Managing.Domain.Scenarios; + +namespace Managing.Domain.Strategies +{ + public class Strategy : IStrategy + { + public Strategy(string name, Timeframe timeframe, StrategyType type) + { + Name = name; + Timeframe = timeframe; + Candles = new List(); + Type = type; + SignalType = ScenarioHelpers.GetSignalType(type); + } + + public string Name { get; set; } + public Timeframe Timeframe { get; set; } + public List Candles { get; set; } + public StrategyType Type { get; set; } + public SignalType SignalType { get; set; } + public int MinimumHistory { get; set; } + public int? Period { get; set; } + public int? FastPeriods { get; set; } + public int? SlowPeriods { get; set; } + public int? SignalPeriods { get; set; } + public double? Multiplier { get; set; } + public int? SmoothPeriods { get; set; } + public int? StochPeriods { get; set; } + public int? CyclePeriods { get; set; } + + public virtual List Run() + { + return new List(); + } + + public void UpdateCandles(HashSet newCandles) + { + lock (Candles) + { + foreach (var item in newCandles.ToList()) + { + if (Candles.All(c => c.Date != item.Date)) + { + Candles.AddItem(item); + } + } + } + } + + public string GetName() + { + return Name; + } + } +} diff --git a/src/Managing.Domain/Strategies/SuperTrendStrategy.cs b/src/Managing.Domain/Strategies/SuperTrendStrategy.cs new file mode 100644 index 0000000..bab8f21 --- /dev/null +++ b/src/Managing.Domain/Strategies/SuperTrendStrategy.cs @@ -0,0 +1,104 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; +using Skender.Stock.Indicators; +using static Managing.Common.Enums; + +namespace Managing.Domain.Strategies; + +public class SuperTrendStrategy : Strategy +{ + public List Signals { get; set; } + + public SuperTrendStrategy(string name, Timeframe timeframe, int period, double multiplier) : base(name, timeframe, StrategyType.SuperTrend) + { + Signals = new List(); + Period = period; + Multiplier = multiplier; + MinimumHistory = 100 + Period.Value; + } + + public override List Run() + { + if (Candles.Count <= MinimumHistory) + { + return null; + } + + try + { + var superTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue).ToList(); + var superTrendCandle = MapSuperTrendToCandle(superTrend, Candles.TakeLast(MinimumHistory)); + + if (superTrendCandle.Count == 0) + return null; + + var previousCandle = superTrendCandle[0]; + foreach (var currentCandle in superTrendCandle.Skip(1)) + { + // Short + if (currentCandle.Close < previousCandle.SuperTrend && previousCandle.Close > previousCandle.SuperTrend) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Short, Confidence.Medium); + } + + // Long + if (currentCandle.Close > previousCandle.SuperTrend && previousCandle.Close < previousCandle.SuperTrend) + { + AddSignal(currentCandle, Timeframe, TradeDirection.Long, Confidence.Medium); + } + + previousCandle = currentCandle; + } + + return Signals.Where(s => s.Confidence != Confidence.None).OrderBy(s => s.Date).ToList(); + } + catch (RuleException) + { + return null; + } + } + + private List MapSuperTrendToCandle(List superTrend, IEnumerable candles) + { + var superTrends = new List(); + foreach (var candle in candles) + { + var currentSuperTrend = superTrend.Find(candle.Date); + if (currentSuperTrend != null) + { + superTrends.Add(new CandleSuperTrend() + { + Close = candle.Close, + Open = candle.Open, + Date = candle.Date, + Ticker = candle.Ticker, + Exchange = candle.Exchange, + SuperTrend = currentSuperTrend.SuperTrend.Value, + LowerBand = currentSuperTrend.LowerBand, + UpperBand = currentSuperTrend.UpperBand, + }); + } + } + + return superTrends; + } + + private void AddSignal(CandleSuperTrend candleSignal, Timeframe timeframe, TradeDirection direction, Confidence confidence) + { + var signal = new Signal(MiscExtensions.ParseEnum(candleSignal.Ticker), direction, confidence, candleSignal, candleSignal.Date, + candleSignal.Exchange, timeframe, Type, SignalType); + if (!Signals.Any(s => s.Identifier == signal.Identifier)) + { + Signals.AddItem(signal); + } + } + + + private class CandleSuperTrend : Candle + { + public decimal SuperTrend { get; internal set; } + public decimal? LowerBand { get; internal set; } + public decimal? UpperBand { get; internal set; } + } +} diff --git a/src/Managing.Domain/Strategies/ThreeWhiteSoldiersStrategy.cs b/src/Managing.Domain/Strategies/ThreeWhiteSoldiersStrategy.cs new file mode 100644 index 0000000..e8b5606 --- /dev/null +++ b/src/Managing.Domain/Strategies/ThreeWhiteSoldiersStrategy.cs @@ -0,0 +1,55 @@ +using Managing.Domain.Candles; +using Managing.Domain.Shared.Rules; +using Managing.Domain.Strategies; +using Managing.Domain.Strategies.Rules; +using static Managing.Common.Enums; + +namespace Managing.Application.Strategies +{ + public class ThreeWhiteSoldiersStrategy : Strategy + { + public ThreeWhiteSoldiersStrategy(string name, Timeframe timeframe, int period) + : base(name, timeframe, StrategyType.ThreeWhiteSoldiers) + { + Period = period; + } + + public TradeDirection Direction { get; } + + public override List Run() + { + var signals = new List(); + + if (Candles.Count <= 3) + { + return null; + } + + try + { + var lastFourCandles = Candles.TakeLast(4); + Candle previousCandles = null; + + foreach (var currentCandle in lastFourCandles) + { + if (Direction == TradeDirection.Long) + { + Check.That(new CloseHigherThanThePreviousHigh(previousCandles, currentCandle)); + } + else + { + Check.That(new CloseLowerThanThePreviousHigh(previousCandles, currentCandle)); + } + + previousCandles = currentCandle; + } + + return signals; + } + catch (RuleException) + { + return null; + } + } + } +} diff --git a/src/Managing.Domain/Trades/Fee.cs b/src/Managing.Domain/Trades/Fee.cs new file mode 100644 index 0000000..33c10de --- /dev/null +++ b/src/Managing.Domain/Trades/Fee.cs @@ -0,0 +1,10 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Trades; + +public class Fee +{ + public decimal Cost { get; set; } + public TradingExchanges Exchange { get; set; } + public DateTime LastUpdate { get; set; } +} diff --git a/src/Managing.Domain/Trades/OrderBookEntry.cs b/src/Managing.Domain/Trades/OrderBookEntry.cs new file mode 100644 index 0000000..182e5ec --- /dev/null +++ b/src/Managing.Domain/Trades/OrderBookEntry.cs @@ -0,0 +1,7 @@ +namespace Managing.Domain.Trades; + +public class OrderBookEntry +{ + public decimal Price { get; set; } + public decimal Quantity { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Domain/Trades/OrderBookExtensions.cs b/src/Managing.Domain/Trades/OrderBookExtensions.cs new file mode 100644 index 0000000..f482934 --- /dev/null +++ b/src/Managing.Domain/Trades/OrderBookExtensions.cs @@ -0,0 +1,35 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Trades; + +public static class OrderBookExtensions +{ + public static decimal GetBestPrice(this Orderbook orderbook, TradeDirection direction, decimal quantity) + { + var entries = direction == TradeDirection.Long ? orderbook.Asks : orderbook.Bids; + var spend = 0m; + var entryIndex = 0; + var matches = new List<(decimal amount, decimal price, decimal sum)>(); + + while (spend < quantity) + { + if (entries[entryIndex].Quantity > quantity - spend) + { + var amount = quantity - spend; + matches.Add((amount, entries[entryIndex].Price, amount * entries[entryIndex].Price)); + spend += amount; + } + else + { + matches.Add((entries[entryIndex].Quantity, entries[entryIndex].Price, entries[entryIndex].Quantity * entries[entryIndex].Price)); + spend += entries[entryIndex].Quantity; + } + + entryIndex++; + } + + var meanPrice = matches.Sum(s => s.sum) / matches.Sum(s => s.amount); + + return meanPrice; + } +} diff --git a/src/Managing.Domain/Trades/Orderbook.cs b/src/Managing.Domain/Trades/Orderbook.cs new file mode 100644 index 0000000..4f98c8c --- /dev/null +++ b/src/Managing.Domain/Trades/Orderbook.cs @@ -0,0 +1,8 @@ +namespace Managing.Domain.Trades; + +public class Orderbook +{ + public List Bids { get; set; } + public List Asks { get; set; } +} + \ No newline at end of file diff --git a/src/Managing.Domain/Trades/Position.cs b/src/Managing.Domain/Trades/Position.cs new file mode 100644 index 0000000..52dfa74 --- /dev/null +++ b/src/Managing.Domain/Trades/Position.cs @@ -0,0 +1,57 @@ +using Managing.Domain.MoneyManagements; +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Trades +{ + public class Position + { + public Position(string accountName, TradeDirection originDirection, Ticker ticker, MoneyManagement moneyManagement, PositionInitiator positionInitiator, DateTime date) + { + Identifier = Guid.NewGuid().ToString(); + AccountName = accountName; + OriginDirection = originDirection; + Ticker = ticker; + MoneyManagement = moneyManagement; + Initiator = positionInitiator; + Date = date; + Status = Initiator == PositionInitiator.PaperTrading ? PositionStatus.Filled : PositionStatus.New; + } + + [Required] + public string AccountName { get; } + [Required] + public DateTime Date { get; set; } + [Required] + public TradeDirection OriginDirection { get; } + [Required] + public Ticker Ticker { get; } + [Required] + public MoneyManagement MoneyManagement { get; } + [Required] + public Trade Open { get; set; } + [Required] + public Trade StopLoss { get; set; } + [Required] + public Trade TakeProfit1 { get; set; } + public Trade TakeProfit2 { get; set; } + public ProfitAndLoss ProfitAndLoss { get; set; } + [Required] + public PositionStatus Status { get; set; } + public string SignalIdentifier { get; set; } + [Required] + public string Identifier { get; set; } + [Required] + public PositionInitiator Initiator { get; } + + public bool IsFinished() + { + return Status switch + { + PositionStatus.Finished => true, + PositionStatus.Flipped => true, + _ => false + }; + } + } +} diff --git a/src/Managing.Domain/Trades/ProfitAndLoss.cs b/src/Managing.Domain/Trades/ProfitAndLoss.cs new file mode 100644 index 0000000..ac98eb9 --- /dev/null +++ b/src/Managing.Domain/Trades/ProfitAndLoss.cs @@ -0,0 +1,102 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Trades +{ + public sealed class ProfitAndLoss + { + public decimal Realized { get; set; } + + public decimal Net { get; set; } + + public decimal AverageOpenPrice { get; private set; } + private const decimal _multiplier = 100000; + + public ProfitAndLoss() + { + } + + public ProfitAndLoss(IEnumerable> initial, TradeDirection direction) + { + decimal buyQuantity = 0, sellQuantity = 0; + decimal averageBuyPrice = 0, averageSellPrice = 0; + foreach (var fill in initial) + { + var quantityFilled = fill.Item1; + if (fill.Item1 > 0) + { + buyQuantity += quantityFilled; + averageBuyPrice += quantityFilled * fill.Item2; + } + else if (fill.Item1 < 0) + { + var absQuantity = Math.Abs(quantityFilled); + sellQuantity += absQuantity; + averageSellPrice += absQuantity * fill.Item2; + } + } + + if (buyQuantity > 0) + averageBuyPrice /= buyQuantity; + if (sellQuantity > 0) + averageSellPrice /= sellQuantity; + + //buyQuantity = (buyQuantity / _multiplier); + //sellQuantity = (sellQuantity / _multiplier); + Net = buyQuantity - sellQuantity; + AverageOpenPrice = Net > 0 ? averageBuyPrice : averageSellPrice; + + var absoluteRealized = (averageSellPrice - averageBuyPrice) * Math.Min(buyQuantity, sellQuantity); + if (direction == TradeDirection.Long) + { + Realized = absoluteRealized; + } else + { + Realized = -absoluteRealized; + } + } + + public void AddFill(decimal quantity, decimal price, TradeDirection direction) + { + if (quantity == 0) + throw new ArgumentOutOfRangeException(nameof(quantity), "Quantity must be non-zero."); + + if (Math.Sign(Net) != Math.Sign(quantity)) + { + decimal absNet = Math.Abs(Net); + decimal absQuantity = Math.Abs(quantity); + decimal realizedResult = 0; + + if (absNet == absQuantity) // flat + { + realizedResult = (price - AverageOpenPrice) * Net; + AverageOpenPrice = 0; + } + else if (absNet > absQuantity) // decrease + { + realizedResult = (price - AverageOpenPrice) * -quantity; + } + else // reverse + { + realizedResult = (price - AverageOpenPrice) * Net; + AverageOpenPrice = price; + } + + if (direction == TradeDirection.Long) + Realized += realizedResult; + else + Realized += -realizedResult; + } + else // increase position + { + AverageOpenPrice = (Net * AverageOpenPrice + quantity * price) / (Net + quantity); + } + + Net += quantity; + } + + public decimal FloatingForTheoriticalExit(decimal exitPrice) + { + return (exitPrice - AverageOpenPrice) * Net; + } + } +} diff --git a/src/Managing.Domain/Trades/Trade.cs b/src/Managing.Domain/Trades/Trade.cs new file mode 100644 index 0000000..3166a82 --- /dev/null +++ b/src/Managing.Domain/Trades/Trade.cs @@ -0,0 +1,69 @@ +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Trades +{ + public class Trade + { + public Trade(DateTime date, TradeDirection direction, TradeStatus status, TradeType tradeType, Ticker ticker, + decimal quantity, decimal price, decimal? leverage, string exchangeOrderId, string message) + { + Date = date; + Direction = direction; + Status = status; + TradeType = tradeType; + Ticker = ticker; + Quantity = quantity; + Price = price; + Leverage = leverage.GetValueOrDefault(); + ExchangeOrderId = exchangeOrderId; + Message = message; + Fee = 0; + } + + public decimal Fee { get; set; } + [Required] + public DateTime Date { get; } + [Required] + public TradeDirection Direction { get; } + [Required] + public TradeStatus Status { get; private set; } + [Required] + public TradeType TradeType { get; } + [Required] + public Ticker Ticker { get; } + [Required] + public decimal Quantity { get; set; } + [Required] + public decimal Price { get; set; } + public decimal Leverage { get; } + [Required] + public string ExchangeOrderId { get; private set; } + public string Message { get; private set; } + + public void SetStatus(TradeStatus status) + { + Status = status; + } + + public void SetExchangeOrderId(string exchangeOrderId) + { + ExchangeOrderId = exchangeOrderId; + } + + public void SetMessage(string message) + { + Message = message; + } + + public void SetQuantity(decimal quantity, int precision) + { + Quantity = Math.Round(quantity, precision); + } + + public void SetPrice(decimal price, int precision) + { + Price = Math.Round(price, precision); + } + } +} diff --git a/src/Managing.Domain/Users/User.cs b/src/Managing.Domain/Users/User.cs new file mode 100644 index 0000000..e27bb5c --- /dev/null +++ b/src/Managing.Domain/Users/User.cs @@ -0,0 +1,9 @@ +using Managing.Domain.Accounts; + +namespace Managing.Domain.Users; + +public class User +{ + public string Name { get; set; } + public List Accounts { get; set; } +} diff --git a/src/Managing.Domain/Workers/Worker.cs b/src/Managing.Domain/Workers/Worker.cs new file mode 100644 index 0000000..6840106 --- /dev/null +++ b/src/Managing.Domain/Workers/Worker.cs @@ -0,0 +1,13 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Workers; + +public class Worker +{ + public WorkerType WorkerType { get; set; } + public DateTime StartTime { get; set; } + public DateTime? LastRunTime { get; set; } + public int ExecutionCount { get; set; } + public TimeSpan Delay { get; set; } + public bool IsActive { get; set; } +} diff --git a/src/Managing.Domain/Workflows/FlowBase.cs b/src/Managing.Domain/Workflows/FlowBase.cs new file mode 100644 index 0000000..c6efc50 --- /dev/null +++ b/src/Managing.Domain/Workflows/FlowBase.cs @@ -0,0 +1,19 @@ +using static Managing.Common.Enums; + +namespace Managing.Domain.Workflows; + +public abstract class FlowBase : IFlow +{ + public abstract Guid Id { get; } + public abstract string Name { get; } + public abstract FlowType Type { get; } + public abstract string Description { get; } + public abstract List AcceptedInputs { get; } + public abstract List Children { get; set; } + public abstract List Parameters { get; set; } + public abstract Guid ParentId { get; } + public abstract string Output { get; set; } + public abstract List OutputTypes { get; } + public abstract Task Execute(string input); + public abstract void MapParameters(); +} diff --git a/src/Managing.Domain/Workflows/FlowParameter.cs b/src/Managing.Domain/Workflows/FlowParameter.cs new file mode 100644 index 0000000..aeff619 --- /dev/null +++ b/src/Managing.Domain/Workflows/FlowParameter.cs @@ -0,0 +1,7 @@ +namespace Managing.Domain.Workflows; + +public class FlowParameter +{ + public dynamic Value { get; set; } + public string Name { get; set; } +} diff --git a/src/Managing.Domain/Workflows/IFlow.cs b/src/Managing.Domain/Workflows/IFlow.cs new file mode 100644 index 0000000..8564bf3 --- /dev/null +++ b/src/Managing.Domain/Workflows/IFlow.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Workflows; + +public interface IFlow +{ + [Required] + Guid Id { get; } + [Required] + string Name { get; } + [Required] + FlowType Type { get; } + [Required] + string Description { get; } + [Required] + List AcceptedInputs { get; } + List Children { get; set; } + [Required] + List Parameters { get; set; } + Guid ParentId { get; } + string Output { get; set; } + [Required] + List OutputTypes { get; } + Task Execute(string input); +} diff --git a/src/Managing.Domain/Workflows/Synthetics/SyntheticFlow.cs b/src/Managing.Domain/Workflows/Synthetics/SyntheticFlow.cs new file mode 100644 index 0000000..cf0ec11 --- /dev/null +++ b/src/Managing.Domain/Workflows/Synthetics/SyntheticFlow.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Workflows.Synthetics; + +public class SyntheticFlow +{ + [Required] + public string Id { get; set; } + public string ParentId { get; set; } + [Required] + public FlowType Type { get; set; } + [Required] + public List Parameters { get; set; } +} diff --git a/src/Managing.Domain/Workflows/Synthetics/SyntheticFlowParameter.cs b/src/Managing.Domain/Workflows/Synthetics/SyntheticFlowParameter.cs new file mode 100644 index 0000000..dd83ff2 --- /dev/null +++ b/src/Managing.Domain/Workflows/Synthetics/SyntheticFlowParameter.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace Managing.Domain.Workflows.Synthetics; + +public class SyntheticFlowParameter +{ + [Required] + public string Value { get; set; } + [Required] + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Domain/Workflows/Synthetics/SyntheticWorkflow.cs b/src/Managing.Domain/Workflows/Synthetics/SyntheticWorkflow.cs new file mode 100644 index 0000000..3834230 --- /dev/null +++ b/src/Managing.Domain/Workflows/Synthetics/SyntheticWorkflow.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Workflows.Synthetics; + +public class SyntheticWorkflow +{ + [Required] + public string Name { get; set; } + [Required] + public WorkflowUsage Usage { get; set; } + [Required] + public string Description { get; set; } + [Required] + public List Flows { get; set; } +} diff --git a/src/Managing.Domain/Workflows/Workflow.cs b/src/Managing.Domain/Workflows/Workflow.cs new file mode 100644 index 0000000..de5e3be --- /dev/null +++ b/src/Managing.Domain/Workflows/Workflow.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using static Managing.Common.Enums; + +namespace Managing.Domain.Workflows; + +public class Workflow +{ + [Required] + public string Name { get; set; } + [Required] + public WorkflowUsage Usage { get; set; } + [Required] + public List Flows { get; set; } + [Required] + public string Description { get; set; } + + public async Task Execute() + { + foreach (var flow in Flows) + { + await flow.Execute(string.Empty); + } + } +} diff --git a/src/Managing.Infrastructure.Database/AccountRepository.cs b/src/Managing.Infrastructure.Database/AccountRepository.cs new file mode 100644 index 0000000..d76a5b2 --- /dev/null +++ b/src/Managing.Infrastructure.Database/AccountRepository.cs @@ -0,0 +1,46 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Domain.Accounts; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; + +namespace Managing.Infrastructure.Databases; + +public class AccountRepository : IAccountRepository +{ + private readonly IMongoRepository _accountRepository; + + public AccountRepository(IMongoRepository accountRepository) + { + _accountRepository = accountRepository; + } + + public void DeleteAccountByName(string name) + { + var account = _accountRepository.FindOne(a => a.Name == name); + _accountRepository.DeleteById(account.Id.ToString()); + } + + public async Task GetAccountByKeyAsync(string key) + { + var account = await _accountRepository.FindOneAsync(a => a.Key == key); + return MongoMappers.Map(account); + } + + public async Task GetAccountByNameAsync(string name) + { + var account = await _accountRepository.FindOneAsync(a => a.Name == name); + return MongoMappers.Map(account); + } + + public IEnumerable GetAccounts() + { + var accounts = _accountRepository.FindAll(); + return MongoMappers.Map(accounts); + } + + public async Task InsertAccountAsync(Account account) + { + await _accountRepository.InsertOneAsync(MongoMappers.Map(account)); + } +} diff --git a/src/Managing.Infrastructure.Database/BacktestRepository.cs b/src/Managing.Infrastructure.Database/BacktestRepository.cs new file mode 100644 index 0000000..101e955 --- /dev/null +++ b/src/Managing.Infrastructure.Database/BacktestRepository.cs @@ -0,0 +1,38 @@ +using Managing.Application.Abstractions; +using Managing.Domain.Backtests; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; + +namespace Managing.Infrastructure.Databases; + +public class BacktestRepository : IBacktestRepository +{ + private readonly IMongoRepository _backtestRepository; + + public BacktestRepository(IMongoRepository backtestRepository) + { + _backtestRepository = backtestRepository; + } + + public void DeleteAllBacktests() + { + _backtestRepository.DropCollection(); + } + + public void DeleteBacktestById(string id) + { + _backtestRepository.DeleteById(id); + } + + public IEnumerable GetBacktests() + { + var backtests = _backtestRepository.FindAll(); + return backtests.Select(b => MongoMappers.Map(b)); + } + + public void InsertBacktest(Backtest backtest) + { + _backtestRepository.InsertOne(MongoMappers.Map(backtest)); + } +} diff --git a/src/Managing.Infrastructure.Database/CandleRepository.cs b/src/Managing.Infrastructure.Database/CandleRepository.cs new file mode 100644 index 0000000..b0547e1 --- /dev/null +++ b/src/Managing.Infrastructure.Database/CandleRepository.cs @@ -0,0 +1,105 @@ +using InfluxDB.Client.Writes; +using Managing.Application.Abstractions.Repositories; +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Infrastructure.Databases.InfluxDb; +using Managing.Infrastructure.Databases.InfluxDb.Models; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases; + +public class CandleRepository : ICandleRepository +{ + private readonly string _priceBucket = "prices-bucket"; + private readonly IInfluxDbRepository _influxDbRepository; + private readonly ILogger _logger; + + public CandleRepository(IInfluxDbRepository influxDbRepository, ILogger logger) + { + _influxDbRepository = influxDbRepository; + _logger = logger; + } + + public async Task> GetCandles( + TradingExchanges exchange, + Ticker ticker, + Timeframe timeframe, + DateTime start) + { + var results = await _influxDbRepository.QueryAsync(async query => + { + var flux = $"from(bucket:\"{_priceBucket}\") " + + $"|> range(start: {start:s}Z) " + + $"|> filter(fn: (r) => r[\"exchange\"] == \"{exchange}\")" + + $"|> filter(fn: (r) => r[\"ticker\"] == \"{ticker}\")" + + $"|> filter(fn: (r) => r[\"timeframe\"] == \"{timeframe}\")" + + $"|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")"; + + var prices = await query.QueryAsync(flux, _influxDbRepository.Organization); + return prices.Select(price => PriceHelpers.Map(price)).ToList(); + }); + + return results; + } + + public async Task> GetTickersAsync( + TradingExchanges exchange, + Timeframe timeframe, + DateTime start) + { + var results = await _influxDbRepository.QueryAsync(async query => + { + var flux = $"from(bucket:\"{_priceBucket}\") " + + $"|> range(start: {start:s}Z, stop: now()) " + + $"|> filter(fn: (r) => r[\"_measurement\"] == \"price\")" + + $"|> filter(fn: (r) => r[\"exchange\"] == \"{exchange}\")" + + $"|> filter(fn: (r) => r[\"timeframe\"] == \"{timeframe}\")" + + $"|> keep(columns: [\"ticker\"])" + + $"|> distinct()"; + + var tickers = new List(); + var records = await query.QueryAsync(flux, _influxDbRepository.Organization); + records.ForEach(table => + { + var fluxRecords = table.Records; + fluxRecords.ForEach(fluxRecord => + { + tickers.AddItem(MiscExtensions.ParseEnum(fluxRecord.GetValueByKey("ticker").ToString())); + }); + }); + + return tickers; + }); + + return results; + } + + public void InsertCandle(Candle candle) + { + _influxDbRepository.Write(write => + { + PriceDto price = PriceHelpers.Map(candle); + write.WriteMeasurement( + price, + InfluxDB.Client.Api.Domain.WritePrecision.Ns, + _priceBucket, + _influxDbRepository.Organization); + }); + } + + public void Test(Candle candle) + { + _influxDbRepository.Write(write => + { + var point = PointData.Measurement(""); + PriceDto price = PriceHelpers.Map(candle); + point.Tag("", ""); + point.Timestamp(price.OpenTime, InfluxDB.Client.Api.Domain.WritePrecision.Ns); + write.WritePoint( + point, + _priceBucket, + _influxDbRepository.Organization); + }); + } +} diff --git a/src/Managing.Infrastructure.Database/InfluxDb/Abstractions/IInfluxDbRepository.cs b/src/Managing.Infrastructure.Database/InfluxDb/Abstractions/IInfluxDbRepository.cs new file mode 100644 index 0000000..b9c6940 --- /dev/null +++ b/src/Managing.Infrastructure.Database/InfluxDb/Abstractions/IInfluxDbRepository.cs @@ -0,0 +1,11 @@ +using InfluxDB.Client; + +namespace Managing.Infrastructure.Databases; + +public interface IInfluxDbRepository +{ + string Organization { get; } + + Task QueryAsync(Func> action); + void Write(Action action); +} diff --git a/src/Managing.Infrastructure.Database/InfluxDb/Abstractions/IInfluxDbSettings.cs b/src/Managing.Infrastructure.Database/InfluxDb/Abstractions/IInfluxDbSettings.cs new file mode 100644 index 0000000..86795fa --- /dev/null +++ b/src/Managing.Infrastructure.Database/InfluxDb/Abstractions/IInfluxDbSettings.cs @@ -0,0 +1,8 @@ +namespace Managing.Infrastructure.Databases.Abstractions; + +public interface IInfluxDbSettings +{ + string Url { get; set; } + string Token { get; set; } + string Organization { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/InfluxDb/InfluxDbRepository.cs b/src/Managing.Infrastructure.Database/InfluxDb/InfluxDbRepository.cs new file mode 100644 index 0000000..564a419 --- /dev/null +++ b/src/Managing.Infrastructure.Database/InfluxDb/InfluxDbRepository.cs @@ -0,0 +1,32 @@ +using InfluxDB.Client; +using Managing.Infrastructure.Databases.Abstractions; + +namespace Managing.Infrastructure.Databases.InfluxDb; + +public class InfluxDbRepository : IInfluxDbRepository +{ + private readonly string _token; + private readonly string _url; + public string Organization { get; set; } + + public InfluxDbRepository(IInfluxDbSettings settings) + { + _token = settings.Token; + _url = settings.Url; + Organization = settings.Organization; + } + + public void Write(Action action) + { + using var client = InfluxDBClientFactory.Create(_url, _token); + using var write = client.GetWriteApi(); + action(write); + } + + public async Task QueryAsync(Func> action) + { + using var client = InfluxDBClientFactory.Create(_url, _token); + var query = client.GetQueryApi(); + return await action(query); + } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/InfluxDb/Models/InfluxDbSettings.cs b/src/Managing.Infrastructure.Database/InfluxDb/Models/InfluxDbSettings.cs new file mode 100644 index 0000000..22dae14 --- /dev/null +++ b/src/Managing.Infrastructure.Database/InfluxDb/Models/InfluxDbSettings.cs @@ -0,0 +1,10 @@ +using Managing.Infrastructure.Databases.Abstractions; + +namespace Managing.Infrastructure.Databases.InfluxDb.Models; + +public class InfluxDbSettings : IInfluxDbSettings +{ + public string Url { get ; set; } + public string Token { get; set; } + public string Organization { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/InfluxDb/Models/PriceDto.cs b/src/Managing.Infrastructure.Database/InfluxDb/Models/PriceDto.cs new file mode 100644 index 0000000..f876d5d --- /dev/null +++ b/src/Managing.Infrastructure.Database/InfluxDb/Models/PriceDto.cs @@ -0,0 +1,36 @@ +using InfluxDB.Client.Core; + +namespace Managing.Infrastructure.Databases.InfluxDb.Models; + +[Measurement("price")] +public class PriceDto +{ + [Column("exchange", IsTag = true)] + public string Exchange { get; set; } + [Column("ticker", IsTag = true)] + public string Ticker { get; set; } + [Column("timeframe", IsTag = true)] + public string Timeframe { get; set; } + [Column("openTime", IsTimestamp = true)] + public DateTime OpenTime { get; set; } + [Column("closeTime", IsTimestamp = true)] + public DateTime CloseTime { get; set; } + [Column("open")] + public decimal Open { get; set; } + [Column("close")] + public decimal Close { get; set; } + [Column("high")] + public decimal High { get; set; } + [Column("low")] + public decimal Low { get; set; } + [Column("baseVolume")] + public decimal BaseVolume { get; set; } + [Column("quoteVolume")] + public decimal QuoteVolume { get; set; } + [Column("TradeCount")] + public int TradeCount { get; set; } + [Column("takerBuyBaseVolume")] + public decimal TakerBuyBaseVolume { get; set; } + [Column("takerBuyQuoteVolume")] + public decimal TakerBuyQuoteVolume { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/InfluxDb/Models/TickerDto.cs b/src/Managing.Infrastructure.Database/InfluxDb/Models/TickerDto.cs new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/src/Managing.Infrastructure.Database/InfluxDb/Models/TickerDto.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/InfluxDb/PriceHelpers.cs b/src/Managing.Infrastructure.Database/InfluxDb/PriceHelpers.cs new file mode 100644 index 0000000..1cb2cbe --- /dev/null +++ b/src/Managing.Infrastructure.Database/InfluxDb/PriceHelpers.cs @@ -0,0 +1,58 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Infrastructure.Databases.InfluxDb.Models; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.InfluxDb; + +public static class PriceHelpers +{ + public static PriceDto Map(Candle candle) + { + var price = new PriceDto + { + Exchange = candle.Exchange.ToString(), + Ticker = candle.Ticker, + OpenTime = candle.OpenTime.ToUniversalTime(), + Open = candle.Open, + Close = candle.Close, + CloseTime = candle.Date.ToUniversalTime(), + High = candle.High, + Low = candle.Low, + BaseVolume = candle.BaseVolume, + QuoteVolume = candle.QuoteVolume, + TradeCount = candle.TradeCount, + TakerBuyBaseVolume = candle.TakerBuyBaseVolume, + TakerBuyQuoteVolume = candle.TakerBuyQuoteVolume, + Timeframe = candle.Timeframe.ToString() + }; + + return price; + } + + public static Candle Map(PriceDto dto) + { + return new Candle + { + Exchange = MiscExtensions.ParseEnum(dto.Exchange), + Ticker = dto.Ticker, + OpenTime = dto.OpenTime, + Open = dto.Open, + Close = dto.Close, + Date = dto.CloseTime, + High = dto.High, + Low = dto.Low, + BaseVolume = dto.BaseVolume, + QuoteVolume = dto.QuoteVolume, + TradeCount = dto.TradeCount, + TakerBuyBaseVolume = dto.TakerBuyBaseVolume, + TakerBuyQuoteVolume = dto.TakerBuyQuoteVolume, + Timeframe = MiscExtensions.ParseEnum(dto.Timeframe) + }; + } + + internal static IEnumerable Map(IEnumerable candles) + { + return candles.Select(candle => Map(candle)); + } +} diff --git a/src/Managing.Infrastructure.Database/Managing.Infrastructure.Databases.csproj b/src/Managing.Infrastructure.Database/Managing.Infrastructure.Databases.csproj new file mode 100644 index 0000000..1588232 --- /dev/null +++ b/src/Managing.Infrastructure.Database/Managing.Infrastructure.Databases.csproj @@ -0,0 +1,21 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/src/Managing.Infrastructure.Database/MongoDb/Abstractions/IMongoRepository.cs b/src/Managing.Infrastructure.Database/MongoDb/Abstractions/IMongoRepository.cs new file mode 100644 index 0000000..db50b0b --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Abstractions/IMongoRepository.cs @@ -0,0 +1,57 @@ +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using MongoDB.Driver; +using System.Linq.Expressions; + +namespace Managing.Infrastructure.Databases.MongoDb.Abstractions +{ + public interface IMongoRepository where TDocument : IDocument + { + IQueryable AsQueryable(); + + IEnumerable FilterBy( + Expression> filterExpression); + + IEnumerable FindAll(); + IEnumerable FilterBy(FilterDefinition filter); + + IEnumerable FilterBy( + Expression> filterExpression, + Expression> projectionExpression); + + TDocument FindOne(Expression> filterExpression); + + Task FindOneAsync(Expression> filterExpression); + + TDocument FindById(string id); + + Task FindByIdAsync(string id); + + void InsertOne(TDocument document); + + Task InsertOneAsync(TDocument document); + + void InsertMany(ICollection documents); + + Task InsertManyAsync(ICollection documents); + + void ReplaceOne(TDocument document); + + Task ReplaceOneAsync(TDocument document); + + void DeleteOne(Expression> filterExpression); + + Task DeleteOneAsync(Expression> filterExpression); + + void DeleteById(string id); + + Task DeleteByIdAsync(string id); + + void DeleteMany(Expression> filterExpression); + + Task DeleteManyAsync(Expression> filterExpression); + + void Update(TDocument entity); + void CreateIndex(string column); + void DropCollection(); + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Attributes/BsonCollectionAttribute.cs b/src/Managing.Infrastructure.Database/MongoDb/Attributes/BsonCollectionAttribute.cs new file mode 100644 index 0000000..801575c --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Attributes/BsonCollectionAttribute.cs @@ -0,0 +1,13 @@ +namespace Managing.Infrastructure.Databases.MongoDb.Attributes +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public class BsonCollectionAttribute : Attribute + { + public string CollectionName { get; } + + public BsonCollectionAttribute(string collectionName) + { + CollectionName = collectionName; + } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/AccountDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/AccountDto.cs new file mode 100644 index 0000000..3a99df6 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/AccountDto.cs @@ -0,0 +1,16 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("Accounts")] +public class AccountDto : Document +{ + public UserDto User { get; set; } + public string Name { get; set; } + public TradingExchanges Exchanges { get; set; } + public AccountType Type { get; set; } + public string Key { get; set; } + public string Secret { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/BacktestDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/BacktestDto.cs new file mode 100644 index 0000000..73dc12f --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/BacktestDto.cs @@ -0,0 +1,26 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("Backtests")] + public class BacktestDto : Document + { + public decimal FinalPnl { get; set; } + public int WinRate { get; set; } + public decimal GrowthPercentage { get; set; } + public decimal HodlPercentage { get; set; } + public Ticker Ticker { get; set; } + public string Scenario { get; set; } + public List Positions { get; set; } + public List Signals { get; set; } + public Timeframe Timeframe { get; set; } + public RiskLevel RiskLevel { get; set; } + public string AccountName { get; set; } + public List Candles { get; set; } + public BotType BotType { get; set; } + public MoneyManagementDto MoneyManagement { get; internal set; } + public MoneyManagementDto OptimizedMoneyManagement { get; internal set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/BadTraderDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/BadTraderDto.cs new file mode 100644 index 0000000..db2284f --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/BadTraderDto.cs @@ -0,0 +1,17 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + + +[BsonCollection("BadTrader")] +public class BadTraderDto : Document +{ + public string Address { get; set; } + public int Winrate { get; set; } + public decimal Pnl { get; set; } + public int TradeCount { get; set; } + public decimal AverageWin { get; set; } + public decimal AverageLoss { get; set; } + public decimal Roi { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/BestTraderDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/BestTraderDto.cs new file mode 100644 index 0000000..52940f9 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/BestTraderDto.cs @@ -0,0 +1,16 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("BestTrader")] +public class BestTraderDto : Document +{ + public string Address { get; set; } + public int Winrate { get; set; } + public decimal Pnl { get; set; } + public int TradeCount { get; set; } + public decimal AverageWin { get; set; } + public decimal AverageLoss { get; set; } + public decimal Roi { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/CandleDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/CandleDto.cs new file mode 100644 index 0000000..1341fb2 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/CandleDto.cs @@ -0,0 +1,28 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using MongoDB.Bson.Serialization.Attributes; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("Candles")] + public class CandleDto : Document + { + public TradingExchanges Exchange { get; set; } + public Timeframe Timeframe { get; set; } + public string Ticker { get; set; } + [BsonDateTimeOptions] + public DateTime OpenTime { get; set; } + [BsonDateTimeOptions] + public DateTime CloseTime { get; set; } + public decimal Open { get; set; } + public decimal Close { get; set; } + public decimal High { get; set; } + public decimal Low { get; set; } + public decimal BaseVolume { get; set; } + public decimal QuoteVolume { get; set; } + public int TradeCount { get; set; } + public decimal TakerBuyBaseVolume { get; set; } + public decimal TakerBuyQuoteVolume { get; set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/FeeDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/FeeDto.cs new file mode 100644 index 0000000..ea2a718 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/FeeDto.cs @@ -0,0 +1,13 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("Fees")] +public class FeeDto : Document +{ + public decimal Cost { get; set; } + public TradingExchanges Exchange { get; set; } + public DateTime LastUpdate { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/MoneyManagementDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/MoneyManagementDto.cs new file mode 100644 index 0000000..7a840d7 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/MoneyManagementDto.cs @@ -0,0 +1,18 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("MoneyManagement")] + public class MoneyManagementDto : Document + { + public Timeframe Timeframe { get; set; } + public RiskLevel RiskLevel { get; set; } + public decimal BalanceAtRisk { get; set; } + public decimal StopLoss { get; set; } + public decimal TakeProfit { get; set; } + public decimal Leverage { get; set; } + public string Name { get; internal set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/PositionDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/PositionDto.cs new file mode 100644 index 0000000..63d4890 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/PositionDto.cs @@ -0,0 +1,27 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using MongoDB.Bson.Serialization.Attributes; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("Positions")] + public class PositionDto : Document + { + [BsonDateTimeOptions] + public DateTime Date { get; set; } + public TradeDto Open { get; set; } + public TradeDto StopLoss { get; set; } + public TradeDto TakeProfit1 { get; set; } + public TradeDto TakeProfit2 { get; set; } + public decimal ProfitAndLoss { get; set; } + public TradeDirection OriginDirection { get; set; } + public string Identifier { get; set; } + public PositionStatus Status { get; set; } + public Ticker Ticker { get; set; } + public string SignalIdentifier { get; set; } + public string AccountName { get; set; } + public MoneyManagementDto MoneyManagement { get; set; } + public PositionInitiator Initiator { get; set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/ScenarioDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/ScenarioDto.cs new file mode 100644 index 0000000..efafcd8 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/ScenarioDto.cs @@ -0,0 +1,12 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("Scenarios")] + public class ScenarioDto : Document + { + public string Name { get; set; } + public List Strategies { get; set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/SignalDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/SignalDto.cs new file mode 100644 index 0000000..644b0eb --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/SignalDto.cs @@ -0,0 +1,21 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("Signals")] + public class SignalDto : Document + { + public TradeDirection Direction { get; set; } + public Confidence Confidence { get; set; } + public DateTime Date { get; set; } + public CandleDto Candle { get; set; } + public string Identifier { get; set; } + public Ticker Ticker { get; set; } + public SignalStatus Status { get; set; } + public Timeframe Timeframe { get; set; } + public StrategyType Type { get; set; } + public SignalType SignalType { get; set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/SpotlighOverviewDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/SpotlighOverviewDto.cs new file mode 100644 index 0000000..9d4551d --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/SpotlighOverviewDto.cs @@ -0,0 +1,30 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("SpotlighOverview")] +public class SpotlighOverviewDto : Document +{ + public List Spotlights { get; set; } + public DateTime DateTime { get; set; } + public Guid Identifier { get; set; } + public int ScenarioCount { get; set; } +} + +public class SpotlightDto +{ + public ScenarioDto Scenario { get; set; } + public List TickerSignals { get; set; } +} + +public class TickerSignalDto +{ + public Ticker Ticker { get; set; } + public List FiveMinutes { get; set; } + public List FifteenMinutes { get; set; } + public List OneHour { get; set; } + public List FourHour { get; set; } + public List OneDay { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/StrategyDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/StrategyDto.cs new file mode 100644 index 0000000..87d2efd --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/StrategyDto.cs @@ -0,0 +1,23 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("Strategies")] + public class StrategyDto : Document + { + public StrategyType Type { get; set; } + public Timeframe Timeframe { get; set; } + public string Name { get; set; } + public int? Period { get; set; } + public int? FastPeriods { get; set; } + public int? SlowPeriods { get; set; } + public int? SignalPeriods { get; set; } + public double? Multiplier { get; set; } + public int? StochPeriods { get; set; } + public int? SmoothPeriods { get; set; } + public int? CyclePeriods { get; set; } + public SignalType SignalType { get; set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/TopVolumeTickerDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/TopVolumeTickerDto.cs new file mode 100644 index 0000000..a805032 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/TopVolumeTickerDto.cs @@ -0,0 +1,15 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("TopVolumeTickers")] +public class TopVolumeTickerDto : Document +{ + public Ticker Ticker { get; set; } + public DateTime Date { get; set; } + public decimal Volume { get; set; } + public int Rank { get; set; } + public TradingExchanges Exchange { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/TradeDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/TradeDto.cs new file mode 100644 index 0000000..6829121 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/TradeDto.cs @@ -0,0 +1,24 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using MongoDB.Bson.Serialization.Attributes; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections +{ + [BsonCollection("Trades")] + public class TradeDto : Document + { + [BsonDateTimeOptions] + public DateTime Date { get; set; } + public TradeDirection Direction { get; set; } + public TradeStatus Status { get; set; } + public TradeType TradeType { get; set; } + public Ticker Ticker { get; set; } + public decimal Fee { get; set; } + public decimal Quantity { get; set; } + public decimal Price { get; set; } + public decimal Leverage { get; set; } + public string ExchangeOrderId { get; set; } + public string Message { get; set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/UserDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/UserDto.cs new file mode 100644 index 0000000..4f94b3b --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/UserDto.cs @@ -0,0 +1,10 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("Users")] +public class UserDto : Document +{ + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/WorkerDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/WorkerDto.cs new file mode 100644 index 0000000..a9e1de2 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/WorkerDto.cs @@ -0,0 +1,16 @@ +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("Workers")] +public class WorkerDto : Document +{ + public WorkerType WorkerType { get; set; } + public DateTime StartTime { get; set; } + public DateTime? LastRunTime { get; set; } + public int ExecutionCount { get; set; } + public TimeSpan Delay { get; set; } + public bool IsActive { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/WorkflowDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/WorkflowDto.cs new file mode 100644 index 0000000..dcc363f --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/WorkflowDto.cs @@ -0,0 +1,16 @@ +using Managing.Domain.Workflows.Synthetics; +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb.Collections; + +[BsonCollection("Workflow")] +public class WorkflowDto : Document +{ + public string Name { get; set; } + public string Description { get; set; } + public WorkflowUsage Usage { get; set; } + public List Flows { get; set; } +} + diff --git a/src/Managing.Infrastructure.Database/MongoDb/Configurations/Document.cs b/src/Managing.Infrastructure.Database/MongoDb/Configurations/Document.cs new file mode 100644 index 0000000..6d3cef5 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Configurations/Document.cs @@ -0,0 +1,13 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Managing.Infrastructure.Databases.MongoDb.Configurations +{ + public abstract class Document : IDocument + { + [BsonId] + public ObjectId Id { get; set; } + [BsonDateTimeOptions] + public DateTime CreatedAt => Id.CreationTime; + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Configurations/IDocument.cs b/src/Managing.Infrastructure.Database/MongoDb/Configurations/IDocument.cs new file mode 100644 index 0000000..46663df --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Configurations/IDocument.cs @@ -0,0 +1,14 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Managing.Infrastructure.Databases.MongoDb.Configurations +{ + public interface IDocument + { + [BsonId] + [BsonRepresentation(BsonType.String)] + ObjectId Id { get; set; } + + DateTime CreatedAt { get; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Configurations/IManagingDatabaseSettings.cs b/src/Managing.Infrastructure.Database/MongoDb/Configurations/IManagingDatabaseSettings.cs new file mode 100644 index 0000000..9b778cb --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Configurations/IManagingDatabaseSettings.cs @@ -0,0 +1,8 @@ +namespace Managing.Infrastructure.Databases.MongoDb +{ + public interface IManagingDatabaseSettings + { + string ConnectionString { get; set; } + string DatabaseName { get; set; } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Configurations/ManagingDatabaseSettings.cs b/src/Managing.Infrastructure.Database/MongoDb/Configurations/ManagingDatabaseSettings.cs new file mode 100644 index 0000000..ce2f7af --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/Configurations/ManagingDatabaseSettings.cs @@ -0,0 +1,7 @@ +namespace Managing.Infrastructure.Databases.MongoDb; + +public class ManagingDatabaseSettings : IManagingDatabaseSettings +{ + public string ConnectionString { get; set; } + public string DatabaseName { get; set; } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/MongoHelpers.cs b/src/Managing.Infrastructure.Database/MongoDb/MongoHelpers.cs new file mode 100644 index 0000000..b0cb9d5 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/MongoHelpers.cs @@ -0,0 +1,20 @@ +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Managing.Infrastructure.Databases.MongoDb +{ + public static class MongoHelpers + { + public static async Task EnsureIndexExists(this IMongoDatabase database, string collectionName, string indexName) + { + var collection = database.GetCollection(collectionName); + var index = new BsonDocument + { + {indexName, 1} + }; + + var indexModel = new CreateIndexModel(index, new CreateIndexOptions { Unique = true }); + await collection.Indexes.CreateOneAsync(indexModel).ConfigureAwait(false); + } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs b/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs new file mode 100644 index 0000000..8412872 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs @@ -0,0 +1,654 @@ +using Managing.Domain.Accounts; +using Managing.Domain.Backtests; +using Managing.Domain.Candles; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Scenarios; +using Managing.Domain.Statistics; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using Managing.Domain.Users; +using Managing.Domain.Workers; +using Managing.Domain.Workflows.Synthetics; +using Managing.Infrastructure.Databases.MongoDb.Collections; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases.MongoDb; + +public static class MongoMappers +{ + #region Statistics + internal static TopVolumeTickerDto Map(TopVolumeTicker topVolumeTicker) + { + return new TopVolumeTickerDto + { + Ticker = topVolumeTicker.Ticker, + Date = topVolumeTicker.Date, + Volume = topVolumeTicker.Volume, + Rank = topVolumeTicker.Rank, + Exchange = topVolumeTicker.Exchange + }; + } + + internal static IList Map(IEnumerable top) + { + return top.Select(topElement => new TopVolumeTicker + { + Ticker = topElement.Ticker, + Date = topElement.Date, + Volume = topElement.Volume, + Rank = topElement.Rank, + Exchange = topElement.Exchange + }).ToList(); + } + + #endregion + + #region Accounts + internal static AccountDto Map(Account request) + { + return new AccountDto + { + Name = request.Name, + Exchanges = request.Exchange, + Key = request.Key, + Secret = request.Secret, + Type = request.Type, + User = Map(request.User) + }; + } + + internal static IEnumerable Map(IEnumerable accounts) + { + return accounts.Select(account => Map(account)); + } + + internal static Account Map(AccountDto account, bool hideKeys = false) + { + if (account == null) return null; + + var a = new Account + { + Name = account.Name, + Exchange = account.Exchanges, + }; + + if (!hideKeys) + { + a.Key = account.Key; + a.Secret = account.Secret; + } + + a.Exchange = account.Exchanges; + a.Type = account.Type; + a.User = Map(account.User); + + return a; + } + #endregion + + #region Workers + internal static WorkerDto Map(Worker worker) + { + return new WorkerDto + { + WorkerType = worker.WorkerType, + StartTime = worker.StartTime, + LastRunTime = worker.LastRunTime, + ExecutionCount = worker.ExecutionCount, + Delay = worker.Delay + }; + } + + internal static Worker Map(WorkerDto worker) + { + if (worker == null) + return null; + + return new Worker + { + WorkerType = worker.WorkerType, + StartTime = worker.StartTime, + LastRunTime = worker.LastRunTime, + ExecutionCount = worker.ExecutionCount, + Delay = worker.Delay, + IsActive = worker.IsActive + }; + } + + #endregion + + #region Backtests + internal static Backtest Map(BacktestDto b) + { + return new Backtest( + ticker: b.Ticker, + scenario: b.Scenario, + positions: b.Positions.ConvertAll(bPosition => Map(bPosition)), + signals: b.Signals != null ? b.Signals.ConvertAll(bSignal => Map(bSignal)) : null, + timeframe: b.Timeframe, + candles: b.Candles.ConvertAll(bCandle => Map(bCandle)), + accountName: b.AccountName, + botType: b.BotType) + { + Id = b.Id.ToString(), + FinalPnl = b.FinalPnl, + WinRate = b.WinRate, + GrowthPercentage = b.GrowthPercentage, + HodlPercentage = b.HodlPercentage, + MoneyManagement = Map(b.MoneyManagement), + OptimizedMoneyManagement = Map(b.OptimizedMoneyManagement) + }; + } + + internal static BacktestDto Map(Backtest result) + { + var backtest = new BacktestDto + { + FinalPnl = result.FinalPnl, + WinRate = result.WinRate, + GrowthPercentage = result.GrowthPercentage, + HodlPercentage = result.HodlPercentage, + Candles = Map(result.Candles), + Positions = Map(result.Positions), + AccountName = result.AccountName, + BotType = result.BotType, + MoneyManagement = Map(result.MoneyManagement), + OptimizedMoneyManagement = Map(result.OptimizedMoneyManagement), + }; + + backtest.Timeframe = result.Timeframe; + backtest.Ticker = result.Ticker; + backtest.Scenario = result.Scenario; + + return backtest; + } + + #endregion + + #region Candles + public static Candle Map(CandleDto candle) + { + if (candle == null) + return null; + + return new Candle() + { + Ticker = candle.Ticker, + BaseVolume = candle.BaseVolume, + Close = candle.Close, + Date = candle.OpenTime, + Open = candle.Open, + OpenTime = candle.OpenTime, + High = candle.High, + Low = candle.Low, + QuoteVolume = candle.QuoteVolume, + TakerBuyBaseVolume = candle.TakerBuyBaseVolume, + TakerBuyQuoteVolume = candle.TakerBuyQuoteVolume, + TradeCount = candle.TradeCount, + Exchange = candle.Exchange, + }; + } + + public static CandleDto Map(Candle candle) + { + return new CandleDto + { + Exchange = candle.Exchange, + Ticker = candle.Ticker, + OpenTime = candle.OpenTime, + Open = candle.Open, + Close = candle.Close, + High = candle.High, + Low = candle.Low, + BaseVolume = candle.BaseVolume, + QuoteVolume = candle.QuoteVolume, + TradeCount = candle.TradeCount, + TakerBuyBaseVolume = candle.TakerBuyBaseVolume, + TakerBuyQuoteVolume = candle.TakerBuyQuoteVolume + }; + } + + public static List Map(List candles) + { + return candles.ConvertAll(candle => Map(candle)); + } + + + #endregion + + #region Positions + public static PositionDto Map(Position position) + { + var p = new PositionDto + { + Date = position.Date, + Open = Map(position.Open), + OriginDirection = position.OriginDirection, + Identifier = position.Identifier, + SignalIdentifier = position.SignalIdentifier, + Status = position.Status, + AccountName = position.AccountName, + MoneyManagement = Map(position.MoneyManagement), + Initiator = position.Initiator, + Ticker = position.Ticker + }; + + if (position.StopLoss != null) + p.StopLoss = Map(position.StopLoss); + + if (position.TakeProfit1 != null) + p.TakeProfit1 = Map(position.TakeProfit1); + + if (position.TakeProfit2 != null) + p.TakeProfit2 = Map(position.TakeProfit2); + + if (position.ProfitAndLoss != null) + p.ProfitAndLoss = position.ProfitAndLoss.Realized; + + return p; + } + + private static TradeDto Map(Trade trade) + { + return new TradeDto + { + Date = trade.Date, + Direction = trade.Direction, + Status = trade.Status, + TradeType = trade.TradeType, + Ticker = trade.Ticker, + Quantity = trade.Quantity, + Price = trade.Price, + Leverage = trade.Leverage, + ExchangeOrderId = trade.ExchangeOrderId, + Message = trade.Message + }; + } + + public static Position Map(PositionDto dto) + { + var position = new Position(dto.AccountName, originDirection: dto.OriginDirection, dto.Ticker, Map(dto.MoneyManagement), dto.Initiator, dto.Date) + { + Open = new Trade(date: dto.Open.Date, direction: dto.Open.Direction, status: dto.Open.Status, tradeType: dto.Open.TradeType, ticker: dto.Open.Ticker, quantity: dto.Open.Quantity, price: dto.Open.Price, leverage: dto.Open.Leverage, exchangeOrderId: dto.Open.ExchangeOrderId, message: dto.Open.Message), + ProfitAndLoss = new ProfitAndLoss { Realized = dto.ProfitAndLoss }, + Status = dto.Status, + SignalIdentifier = dto.SignalIdentifier, + Identifier = dto.Identifier + }; + + if (dto.StopLoss != null) + { + position.StopLoss = new Trade(date: dto.StopLoss.Date, direction: dto.StopLoss.Direction, status: dto.StopLoss.Status, tradeType: dto.StopLoss.TradeType, ticker: dto.StopLoss.Ticker, quantity: dto.StopLoss.Quantity, price: dto.StopLoss.Price, leverage: dto.StopLoss.Leverage, exchangeOrderId: dto.StopLoss.ExchangeOrderId, message: dto.StopLoss.Message); + } + + if (dto.TakeProfit1 != null) + { + position.TakeProfit1 = new Trade(date: dto.TakeProfit1.Date, direction: dto.TakeProfit1.Direction, status: dto.TakeProfit1.Status, tradeType: dto.TakeProfit1.TradeType, ticker: dto.TakeProfit1.Ticker, quantity: dto.TakeProfit1.Quantity, price: dto.TakeProfit1.Price, leverage: dto.TakeProfit1.Leverage, exchangeOrderId: dto.TakeProfit1.ExchangeOrderId, message: dto.TakeProfit1.Message); + } + + if (dto.TakeProfit2 != null) + { + position.TakeProfit2 = new Trade(date: dto.TakeProfit2.Date, direction: dto.TakeProfit2.Direction, status: dto.TakeProfit2.Status, tradeType: dto.TakeProfit2.TradeType, ticker: dto.TakeProfit2.Ticker, quantity: dto.TakeProfit2.Quantity, price: dto.TakeProfit2.Price, leverage: dto.TakeProfit2.Leverage, exchangeOrderId: dto.TakeProfit2.ExchangeOrderId, message: dto.TakeProfit2.Message); + } + + return position; + } + + internal static List Map(List positions) + { + return positions.ConvertAll(position => Map(position)); + } + + #endregion + + #region Signals + public static SignalDto Map(Signal signal) + { + return new SignalDto + { + Identifier = signal.Identifier, + Direction = signal.Direction, + Candle = Map(signal.Candle), + Confidence = signal.Confidence, + Date = signal.Date, + Ticker = signal.Ticker, + Status = signal.Status, + Timeframe = signal.Timeframe, + Type = signal.StrategyType + }; + } + + internal static Signal Map(SignalDto bSignal) + { + return new Signal(ticker: bSignal.Ticker, direction: bSignal.Direction, confidence: bSignal.Confidence, + candle: Map(bSignal.Candle), date: bSignal.Date, exchange: default, timeframe: bSignal.Timeframe, + strategyType: bSignal.Type, signalType: bSignal.SignalType) + { + Status = bSignal.Status + }; + } + + #endregion + + #region Scenarios + public static ScenarioDto Map(Scenario scenario) + { + return new ScenarioDto() + { + Name = scenario.Name, + Strategies = Map(scenario.Strategies), + }; + } + + internal static IEnumerable Map(IEnumerable dtos) + { + return dtos.Select(d => Map(d)); + } + + internal static Scenario Map(ScenarioDto d) + { + return new Scenario(d.Name) + { + Name = d.Name, + Strategies = Map(d.Strategies).ToList() + }; + } + + private static List Map(List strategies) + { + return strategies.ConvertAll(strategy => Map(strategy)); + } + + internal static Strategy Map(StrategyDto strategyDto) + { + return new Strategy(name: strategyDto.Name, timeframe: strategyDto.Timeframe, type: strategyDto.Type) + { + Period = strategyDto.Period, + FastPeriods = strategyDto.FastPeriods, + SlowPeriods = strategyDto.SlowPeriods, + SignalPeriods = strategyDto.SignalPeriods, + Multiplier = strategyDto.Multiplier, + SmoothPeriods = strategyDto.SmoothPeriods, + StochPeriods = strategyDto.StochPeriods, + CyclePeriods = strategyDto.CyclePeriods + }; + } + internal static StrategyDto Map(Strategy strategy) + { + var dto = new StrategyDto + { + Type = strategy.Type, + Timeframe = strategy.Timeframe, + Name = strategy.Name, + SignalType = strategy.SignalType + }; + + switch (strategy.Type) + { + case StrategyType.RsiDivergenceConfirm: + case StrategyType.RsiDivergence: + case StrategyType.EmaCross: + case StrategyType.EmaTrend: + case StrategyType.StDev: + dto.Period = strategy.Period; + break; + case StrategyType.MacdCross: + dto.SlowPeriods = strategy.SlowPeriods; + dto.FastPeriods = strategy.FastPeriods; + dto.SignalPeriods = strategy.SignalPeriods; + break; + case StrategyType.ThreeWhiteSoldiers: + break; + case StrategyType.ChandelierExit: + case StrategyType.SuperTrend: + dto.Period = strategy.Period; + dto.Multiplier = strategy.Multiplier; + break; + case StrategyType.StochRsiTrend: + dto.Period = strategy.Period; + dto.StochPeriods = strategy.StochPeriods; + dto.SignalPeriods = strategy.SignalPeriods; + dto.SmoothPeriods = strategy.SmoothPeriods; + break; + case StrategyType.Stc: + dto.SlowPeriods = strategy.SlowPeriods; + dto.FastPeriods = strategy.FastPeriods; + dto.CyclePeriods = strategy.CyclePeriods; + break; + default: + break; + } + + return dto; + } + + internal static IEnumerable Map(IEnumerable strategies) + { + return strategies.Select(strategy => Map(strategy)); + } + + #endregion + + #region Money Management + public static MoneyManagementDto Map(MoneyManagement request) + { + if (request == null) return null; + return new MoneyManagementDto + { + Timeframe = request.Timeframe, + BalanceAtRisk = request.BalanceAtRisk, + StopLoss = request.StopLoss, + TakeProfit = request.TakeProfit, + Leverage = request.Leverage, + Name = request.Name + }; + } + + public static MoneyManagement Map(MoneyManagementDto request) + { + if (request == null) + return null; + + return new MoneyManagement + { + Timeframe = request.Timeframe, + BalanceAtRisk = request.BalanceAtRisk, + StopLoss = request.StopLoss, + TakeProfit = request.TakeProfit, + Leverage = request.Leverage, + Name = request.Name + }; + } + + internal static User Map(UserDto user) + { + if (user == null) + return null; + + return new User + { + Name = user.Name, + }; + } + + internal static UserDto Map(User user) + { + return new UserDto + { + Name = user.Name + }; + } + + internal static SpotlighOverviewDto Map(SpotlightOverview overview) + { + return new SpotlighOverviewDto + { + Spotlights = Map(overview.Spotlights), + DateTime = overview.DateTime, + Identifier = overview.Identifier, + ScenarioCount = overview.ScenarioCount, + }; + } + + private static List Map(List spotlights) + { + return spotlights.ConvertAll(spotlight => new SpotlightDto + { + Scenario = new ScenarioDto + { + Name = spotlight.Scenario.Name, + Strategies = spotlight.Scenario.Strategies.ConvertAll(spotlightScenarioStrategy => Map(spotlightScenarioStrategy)) + }, + TickerSignals = spotlight.TickerSignals.ConvertAll(spotlightTickerSignal => new TickerSignalDto + { + Ticker = spotlightTickerSignal.Ticker, + FiveMinutes = spotlightTickerSignal.FiveMinutes?.ConvertAll(spotlightTickerSignalFiveMinute => Map(spotlightTickerSignalFiveMinute)) ?? new List(), + FifteenMinutes = spotlightTickerSignal.FifteenMinutes?.ConvertAll(spotlightTickerSignalFifteenMinute => Map(spotlightTickerSignalFifteenMinute)) ?? new List(), + OneHour = spotlightTickerSignal.OneHour?.ConvertAll(spotlightTickerSignalOneHour => Map(spotlightTickerSignalOneHour)) ?? new List(), + FourHour = spotlightTickerSignal.FourHour?.ConvertAll(spotlightTickerSignalFourHour => Map(spotlightTickerSignalFourHour)) ?? new List(), + OneDay = spotlightTickerSignal.OneDay?.ConvertAll(spotlightTickerSignalOneDay => Map(spotlightTickerSignalOneDay)) ?? new List() + }) + }); + } + + internal static SpotlightOverview Map(SpotlighOverviewDto overview) + { + return new SpotlightOverview + { + Spotlights = Map(overview.Spotlights), + DateTime = overview.DateTime, + Identifier = overview.Identifier, + ScenarioCount = overview.ScenarioCount + }; + } + + private static List Map(List spotlights) + { + return spotlights.ConvertAll(spotlight => new Spotlight + { + Scenario = new Scenario(name: spotlight.Scenario.Name) + { + Strategies = spotlight.Scenario.Strategies.ConvertAll(spotlightScenarioStrategy => Map(spotlightScenarioStrategy)) + }, + TickerSignals = spotlight.TickerSignals.ConvertAll(spotlightTickerSignal => new TickerSignal + { + Ticker = spotlightTickerSignal.Ticker, + FiveMinutes = spotlightTickerSignal.FiveMinutes.ConvertAll(spotlightTickerSignalFiveMinute => Map(spotlightTickerSignalFiveMinute)), + FifteenMinutes = spotlightTickerSignal.FifteenMinutes.ConvertAll(spotlightTickerSignalFifteenMinute => Map(spotlightTickerSignalFifteenMinute)), + OneHour = spotlightTickerSignal.OneHour.ConvertAll(spotlightTickerSignalOneHour => Map(spotlightTickerSignalOneHour)), + FourHour = spotlightTickerSignal.FourHour.ConvertAll(spotlightTickerSignalFourHour => Map(spotlightTickerSignalFourHour)), + OneDay = spotlightTickerSignal.OneDay.ConvertAll(spotlightTickerSignalOneDay => Map(spotlightTickerSignalOneDay)) + }) + }); + } + + internal static IList Map(IEnumerable overviews) + { + return overviews.Select(Map).ToList(); + } + + + internal static FeeDto Map(Fee fee) + { + return new FeeDto + { + Cost = fee.Cost, + Exchange = fee.Exchange, + LastUpdate = fee.LastUpdate + }; + } + + internal static Fee Map(FeeDto fee) + { + if (fee == null) return null; + + return new Fee + { + Cost = fee.Cost, + Exchange = fee.Exchange, + LastUpdate = fee.LastUpdate + }; + } + + internal static List Map(IEnumerable enumerable) + { + return enumerable.Select(enumerableElement => new Trader + { + Address = enumerableElement.Address, + Winrate = enumerableElement.Winrate, + Pnl = enumerableElement.Pnl, + TradeCount = enumerableElement.TradeCount, + AverageWin = enumerableElement.AverageWin, + AverageLoss = enumerableElement.AverageLoss, + Roi = enumerableElement.Roi + }).ToList(); + } + + internal static BestTraderDto Map(Trader trader) + { + return new BestTraderDto + { + Address = trader.Address, + Winrate = trader.Winrate, + Pnl = trader.Pnl, + TradeCount = trader.TradeCount, + AverageWin = trader.AverageWin, + AverageLoss = trader.AverageLoss, + Roi = trader.Roi + }; + } + + internal static List Map(IEnumerable enumerable) + { + return enumerable.Select(enumerableElement => new Trader + { + Address = enumerableElement.Address, + Winrate = enumerableElement.Winrate, + Pnl = enumerableElement.Pnl, + TradeCount = enumerableElement.TradeCount, + AverageWin = enumerableElement.AverageWin, + AverageLoss = enumerableElement.AverageLoss, + Roi = enumerableElement.Roi + }).ToList(); + } + + internal static BadTraderDto BadTraderMap(Trader trader) + { + return new BadTraderDto + { + Address = trader.Address, + Winrate = trader.Winrate, + Pnl = trader.Pnl, + TradeCount = trader.TradeCount, + AverageWin = trader.AverageWin, + AverageLoss = trader.AverageLoss, + Roi = trader.Roi + }; + } + + internal static WorkflowDto Map(SyntheticWorkflow workflow) + { + return new WorkflowDto + { + Name = workflow.Name, + Description = workflow.Description, + Usage = workflow.Usage, + Flows = workflow.Flows + }; + } + + internal static SyntheticWorkflow Map(WorkflowDto m) + { + if (m == null) return null; + + return new SyntheticWorkflow + { + Name = m.Name, + Usage = m.Usage, + Description = m.Description, + Flows = m.Flows.ToList(), + }; + } + + #endregion +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/MongoRepository.cs b/src/Managing.Infrastructure.Database/MongoDb/MongoRepository.cs new file mode 100644 index 0000000..6e36ee7 --- /dev/null +++ b/src/Managing.Infrastructure.Database/MongoDb/MongoRepository.cs @@ -0,0 +1,178 @@ +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Attributes; +using Managing.Infrastructure.Databases.MongoDb.Configurations; +using MongoDB.Bson; +using MongoDB.Driver; +using System.Linq.Expressions; + +namespace Managing.Infrastructure.Databases.MongoDb +{ + public class MongoRepository : IMongoRepository + where TDocument : IDocument + { + private readonly IMongoCollection _collection; + private readonly IMongoDatabase _database; + + public MongoRepository(IManagingDatabaseSettings settings) + { + _database = new MongoClient(settings.ConnectionString).GetDatabase(settings.DatabaseName); + _collection = _database.GetCollection(GetCollectionName(typeof(TDocument))); + } + + private protected string GetCollectionName(Type documentType) + { + return ((BsonCollectionAttribute)documentType.GetCustomAttributes( + typeof(BsonCollectionAttribute), + true) + .FirstOrDefault())?.CollectionName; + } + + public virtual IQueryable AsQueryable() + { + return _collection.AsQueryable(); + } + + public virtual IEnumerable FilterBy( + Expression> filterExpression) + { + return _collection.Find(filterExpression).ToEnumerable(); + } + + public virtual IEnumerable FilterBy(FilterDefinition filter) + { + return _collection.Find(filter).ToEnumerable(); + } + + public virtual IEnumerable FindAll() + { + return _collection.Find(_ => true).ToEnumerable(); + } + + public virtual IEnumerable FilterBy( + Expression> filterExpression, + Expression> projectionExpression) + { + return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable(); + } + + public virtual TDocument FindOne(Expression> filterExpression) + { + return _collection.Find(filterExpression).FirstOrDefault(); + } + + public virtual Task FindOneAsync(Expression> filterExpression) + { + return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync()); + } + + public virtual TDocument FindById(string id) + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + return _collection.Find(filter).SingleOrDefault(); + } + + public virtual Task FindByIdAsync(string id) + { + return Task.Run(() => + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + return _collection.Find(filter).SingleOrDefaultAsync(); + }); + } + + public virtual void InsertOne(TDocument document) + { + _collection.InsertOne(document); + } + + public virtual Task InsertOneAsync(TDocument document) + { + return Task.Run(() => _collection.InsertOneAsync(document)); + } + + public void InsertMany(ICollection documents) + { + _collection.InsertMany(documents); + } + + public void DropCollection() + { + _database.DropCollection(GetCollectionName(typeof(TDocument))); + } + + public virtual async Task InsertManyAsync(ICollection documents) + { + await _collection.InsertManyAsync(documents); + } + + public void ReplaceOne(TDocument document) + { + var filter = Builders.Filter.Eq(doc => doc.Id, document.Id); + _collection.FindOneAndReplace(filter, document); + } + + public virtual async Task ReplaceOneAsync(TDocument document) + { + var filter = Builders.Filter.Eq(doc => doc.Id, document.Id); + await _collection.FindOneAndReplaceAsync(filter, document); + } + + public virtual void Update(TDocument entity) + { + if (entity.Id == ObjectId.Empty) + { + entity.Id = ObjectId.GenerateNewId(); + } + + var option = new ReplaceOptions { IsUpsert = true }; + _collection.ReplaceOne(x => entity != null && x.Id == entity.Id, entity, option); + } + + public virtual void DeleteOne(Expression> filterExpression) + { + _collection.FindOneAndDelete(filterExpression); + } + + public virtual Task DeleteOneAsync(Expression> filterExpression) + { + return Task.Run(() => _collection.FindOneAndDeleteAsync(filterExpression)); + } + + public virtual void DeleteById(string id) + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + _collection.FindOneAndDelete(filter); + } + + public virtual Task DeleteByIdAsync(string id) + { + return Task.Run(() => + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + _collection.FindOneAndDeleteAsync(filter); + }); + } + + public virtual void DeleteMany(Expression> filterExpression) + { + _collection.DeleteMany(filterExpression); + } + + public virtual Task DeleteManyAsync(Expression> filterExpression) + { + return _collection.DeleteManyAsync(filterExpression); + } + + public virtual void CreateIndex(string column) + { + var keys = Builders.IndexKeys.Ascending(column); + var indexOptions = new CreateIndexOptions { Unique = true }; + var model = new CreateIndexModel(keys, indexOptions); + _collection.Indexes.CreateOne(model); + } + } +} diff --git a/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/MoneyManagement.bson.gz b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/MoneyManagement.bson.gz new file mode 100644 index 0000000..cb79d13 Binary files /dev/null and b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/MoneyManagement.bson.gz differ diff --git a/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/MoneyManagement.metadata.json.gz b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/MoneyManagement.metadata.json.gz new file mode 100644 index 0000000..cf75afd Binary files /dev/null and b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/MoneyManagement.metadata.json.gz differ diff --git a/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Scenarios.bson.gz b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Scenarios.bson.gz new file mode 100644 index 0000000..eb949a5 Binary files /dev/null and b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Scenarios.bson.gz differ diff --git a/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Scenarios.metadata.json.gz b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Scenarios.metadata.json.gz new file mode 100644 index 0000000..c98ef8b Binary files /dev/null and b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Scenarios.metadata.json.gz differ diff --git a/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Strategies.bson.gz b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Strategies.bson.gz new file mode 100644 index 0000000..03393fe Binary files /dev/null and b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Strategies.bson.gz differ diff --git a/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Strategies.metadata.json.gz b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Strategies.metadata.json.gz new file mode 100644 index 0000000..743fc31 Binary files /dev/null and b/src/Managing.Infrastructure.Database/MongoDb/Seeds/ManagingDb/Strategies.metadata.json.gz differ diff --git a/src/Managing.Infrastructure.Database/SettingsRepository.cs b/src/Managing.Infrastructure.Database/SettingsRepository.cs new file mode 100644 index 0000000..c1a1570 --- /dev/null +++ b/src/Managing.Infrastructure.Database/SettingsRepository.cs @@ -0,0 +1,53 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Domain.MoneyManagements; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; + +namespace Managing.Infrastructure.Databases; + +public class SettingsRepository : ISettingsRepository +{ + private readonly IMongoRepository _moneyManagementRepository; + + public SettingsRepository(IMongoRepository moneyManagementRepository) + { + _moneyManagementRepository = moneyManagementRepository; + } + + public void DeleteMoneyManagement(string name) + { + var moneyManagement = _moneyManagementRepository.FindOne(m => m.Name == name); + _moneyManagementRepository.DeleteById(moneyManagement.Id.ToString()); + } + + public void DeleteMoneyManagements() + { + _moneyManagementRepository.DropCollection(); + } + + public async Task GetMoneyManagement(string name) + { + var moneyManagement = await _moneyManagementRepository.FindOneAsync(m => m.Name == name); + return MongoMappers.Map(moneyManagement); + } + + public IEnumerable GetMoneyManagements() + { + var moneyManagements = _moneyManagementRepository.FindAll(); + return moneyManagements.Select(m => MongoMappers.Map(m)); + } + + public async Task InsertMoneyManagement(MoneyManagement request) + { + await _moneyManagementRepository.InsertOneAsync(MongoMappers.Map(request)); + } + + public void UpdateMoneyManagement(MoneyManagement moneyManagement) + { + var mm = _moneyManagementRepository.FindOne(m => m.Name == moneyManagement.Name); + var dto = MongoMappers.Map(moneyManagement); + dto.Id = mm.Id; + _moneyManagementRepository.Update(dto); + } +} diff --git a/src/Managing.Infrastructure.Database/StatisticRepository.cs b/src/Managing.Infrastructure.Database/StatisticRepository.cs new file mode 100644 index 0000000..7de54b7 --- /dev/null +++ b/src/Managing.Infrastructure.Database/StatisticRepository.cs @@ -0,0 +1,103 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Domain.Statistics; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; + +namespace Managing.Infrastructure.Databases; + +public class StatisticRepository : IStatisticRepository +{ + private readonly IMongoRepository _bestTrader; + private readonly IMongoRepository _badTrader; + private readonly IMongoRepository _topRepository; + private readonly IMongoRepository _spotlightRepository; + + public StatisticRepository( + IMongoRepository topRepository, + IMongoRepository spotlightRepository, + IMongoRepository bestTrader, + IMongoRepository badTrader) + { + _topRepository = topRepository; + _spotlightRepository = spotlightRepository; + _bestTrader = bestTrader; + _badTrader = badTrader; + } + + public async Task InsertTopVolumeTicker(TopVolumeTicker topVolumeTicker) + { + await _topRepository.InsertOneAsync(MongoMappers.Map(topVolumeTicker)); + } + + public IList GetTopVolumeTickers(DateTime date) + { + var top = _topRepository.FilterBy(t => date < t.Date); + return MongoMappers.Map(top); + } + + public async Task SaveSpotligthtOverview(SpotlightOverview overview) + { + await _spotlightRepository.InsertOneAsync(MongoMappers.Map(overview)); + } + + public IList GetSpotlightOverviews(DateTime date) + { + var overviews = _spotlightRepository.FilterBy(t => date < t.DateTime); + return MongoMappers.Map(overviews); + } + + public void UpdateSpotlightOverview(SpotlightOverview overview) + { + var s = _spotlightRepository.FindOne(o => o.Identifier == overview.Identifier); + var dto = MongoMappers.Map(overview); + dto.Id = s.Id; + _spotlightRepository.Update(dto); + } + + public List GetBestTraders() + { + return MongoMappers.Map(_bestTrader.FindAll()); + } + + public void UpdateBestTrader(Trader trader) + { + var t = _bestTrader.FindOne(t => t.Address == trader.Address); + var dto = MongoMappers.Map(trader); + dto.Id = t.Id; + _bestTrader.Update(dto); + } + + public async Task InsertBestTrader(Trader trader) + { + await _bestTrader.InsertOneAsync(MongoMappers.Map(trader)); + } + + public async Task RemoveBestTrader(Trader trader) + { + await _bestTrader.DeleteOneAsync(t => t.Address == trader.Address); + } + + public List GetBadTraders() + { + return MongoMappers.Map(_badTrader.FindAll()); + } + + public void UpdateBadTrader(Trader trader) + { + var t = _badTrader.FindOne(t => t.Address == trader.Address); + var dto = MongoMappers.BadTraderMap(trader); + dto.Id = t.Id; + _badTrader.Update(dto); + } + + public async Task InsertBadTrader(Trader trader) + { + await _badTrader.InsertOneAsync(MongoMappers.BadTraderMap(trader)); + } + + public async Task RemoveBadTrader(Trader trader) + { + await _badTrader.DeleteOneAsync(t => t.Address == trader.Address); + } +} diff --git a/src/Managing.Infrastructure.Database/TradingRepository.cs b/src/Managing.Infrastructure.Database/TradingRepository.cs new file mode 100644 index 0000000..71006b4 --- /dev/null +++ b/src/Managing.Infrastructure.Database/TradingRepository.cs @@ -0,0 +1,150 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Common; +using Managing.Domain.Scenarios; +using Managing.Domain.Strategies; +using Managing.Domain.Trades; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; +using MongoDB.Driver; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Databases; + +public class TradingRepository : ITradingRepository +{ + private readonly IMongoRepository _scenarioRepository; + private readonly IMongoRepository _signalRepository; + private readonly IMongoRepository _positionRepository; + private readonly IMongoRepository _strategyRepository; + private readonly IMongoRepository _feeRepository; + + + public TradingRepository( + IMongoRepository scenarioRepository, + IMongoRepository signalRepository, + IMongoRepository positionRepository, + IMongoRepository strategyRepository, + IMongoRepository feeRepository) + { + _scenarioRepository = scenarioRepository; + _signalRepository = signalRepository; + _positionRepository = positionRepository; + _strategyRepository = strategyRepository; + _feeRepository = feeRepository; + } + + public void DeleteScenario(string name) + { + var scenario = _scenarioRepository.FindOne(s => s.Name == name); + _scenarioRepository.DeleteById(scenario.Id.ToString()); + } + + public void DeleteScenarios() + { + _scenarioRepository.DropCollection(); + } + + public void DeleteStrategies() + { + _strategyRepository.DropCollection(); + } + + public void DeleteStrategy(string name) + { + var strategy = _strategyRepository.FindOne(s => s.Name == name); + _strategyRepository.DeleteById(strategy.Id.ToString()); + } + + public Position GetPositionByIdentifier(string identifier) + { + var position = _positionRepository.FindOne(p => p.Identifier == identifier); + return MongoMappers.Map(position); + } + + public IEnumerable GetPositions(PositionInitiator positionInitiator) + { + var filter = Builders.Filter.Eq(p => p.Initiator, positionInitiator); + var positions = _positionRepository.FilterBy(filter); + return positions.Select(MongoMappers.Map); + } + + public IEnumerable GetPositionsByStatus(Enums.PositionStatus positionStatus) + { + var filter = Builders.Filter.Eq(p => p.Status, positionStatus); + var positions = _positionRepository.FilterBy(filter); + return positions.Select(MongoMappers.Map); + } + + public Scenario GetScenarioByName(string name) + { + var scenario = _scenarioRepository.FindOne(s => s.Name == name); + return MongoMappers.Map(scenario); + } + + public IEnumerable GetScenarios() + { + var scenarios = _scenarioRepository.FindAll(); + return scenarios.Select(s => MongoMappers.Map(s)); + } + + public IEnumerable GetStrategies() + { + var strategies = _strategyRepository.FindAll(); + return strategies.Select(MongoMappers.Map); + } + + public Strategy GetStrategyByName(string name) + { + var strategy = _strategyRepository.FindOne(s => s.Name == name); + return MongoMappers.Map(strategy); + } + + public void InsertPosition(Position position) + { + _positionRepository.InsertOne(MongoMappers.Map(position)); + } + + public void InsertScenario(Scenario scenario) + { + _scenarioRepository.CreateIndex(nameof(Scenario.Name)); + _scenarioRepository.InsertOne(MongoMappers.Map(scenario)); + } + + public void InsertSignal(Signal signal) + { + _signalRepository.InsertOne(MongoMappers.Map(signal)); + } + + public void InsertStrategy(Strategy strategy) + { + _strategyRepository.InsertOne(MongoMappers.Map(strategy)); + } + + public void UpdatePosition(Position position) + { + var p = _positionRepository.FindOne(p => p.Open.ExchangeOrderId == position.Open.ExchangeOrderId); + var dto = MongoMappers.Map(position); + dto.Id = p.Id; + _positionRepository.Update(dto); + } + + public Fee GetFee(TradingExchanges exchange) + { + var fee = _feeRepository.FindOne(f => f.Exchange == exchange); + return MongoMappers.Map(fee); + } + + public void InsertFee(Fee fee) + { + _feeRepository.InsertOne(MongoMappers.Map(fee)); + } + + public void UpdateFee(Fee fee) + { + var f = _feeRepository.FindOne(f => f.Exchange == fee.Exchange); + var dto = MongoMappers.Map(fee); + dto.Id = f.Id; + _feeRepository.Update(dto); + } +} diff --git a/src/Managing.Infrastructure.Database/UserRepository.cs b/src/Managing.Infrastructure.Database/UserRepository.cs new file mode 100644 index 0000000..b74647f --- /dev/null +++ b/src/Managing.Infrastructure.Database/UserRepository.cs @@ -0,0 +1,28 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Domain.Users; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; + +namespace Managing.Infrastructure.Databases; + +public class UserRepository : IUserRepository +{ + private readonly IMongoRepository _userRepository; + + public UserRepository(IMongoRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task GetUserByNameAsync(string name) + { + var user = await _userRepository.FindOneAsync(u => u.Name == name); + return MongoMappers.Map(user); + } + + public async Task InsertUserAsync(User user) + { + await _userRepository.InsertOneAsync(MongoMappers.Map(user)); + } +} diff --git a/src/Managing.Infrastructure.Database/WorkerRepository.cs b/src/Managing.Infrastructure.Database/WorkerRepository.cs new file mode 100644 index 0000000..f684ee9 --- /dev/null +++ b/src/Managing.Infrastructure.Database/WorkerRepository.cs @@ -0,0 +1,60 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Common; +using Managing.Domain.Workers; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; + +namespace Managing.Infrastructure.Databases; + +public class WorkerRepository : IWorkerRepository +{ + private readonly IMongoRepository _workerRepository; + public WorkerRepository(IMongoRepository workerRepository) + { + _workerRepository = workerRepository; + } + + public async Task DisableWorker(Enums.WorkerType workerType) + { + var worker = await GetWorker(workerType); + worker.IsActive = false; + _workerRepository.Update(worker); + } + + public async Task EnableWorker(Enums.WorkerType workerType) + { + var worker = await GetWorker(workerType); + worker.IsActive = true; + _workerRepository.Update(worker); + } + + public async Task GetWorkerAsync(Enums.WorkerType workerType) + { + return MongoMappers.Map(await GetWorker(workerType)); + } + + public async Task InsertWorker(Worker worker) + { + await _workerRepository.InsertOneAsync(MongoMappers.Map(worker)); + } + + public async Task UpdateWorker(Enums.WorkerType workerType, int executionCount) + { + var worker = await GetWorker(workerType); + worker.ExecutionCount = executionCount; + worker.LastRunTime = DateTime.UtcNow; + _workerRepository.Update(worker); + } + + public IEnumerable GetWorkers() + { + var workers = _workerRepository.FindAll(); + return workers.Select(MongoMappers.Map); + } + + private async Task GetWorker(Enums.WorkerType workerType) + { + return await _workerRepository.FindOneAsync(w => w.WorkerType == workerType); + } +} diff --git a/src/Managing.Infrastructure.Database/WorkflowRepository.cs b/src/Managing.Infrastructure.Database/WorkflowRepository.cs new file mode 100644 index 0000000..e6faaf4 --- /dev/null +++ b/src/Managing.Infrastructure.Database/WorkflowRepository.cs @@ -0,0 +1,48 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Domain.Workflows.Synthetics; +using Managing.Infrastructure.Databases.MongoDb; +using Managing.Infrastructure.Databases.MongoDb.Abstractions; +using Managing.Infrastructure.Databases.MongoDb.Collections; + +namespace Managing.Infrastructure.Databases; + +public class WorkflowRepository : IWorkflowRepository +{ + private readonly IMongoRepository _workflowRepository; + + public WorkflowRepository(IMongoRepository workflowRepository) + { + _workflowRepository = workflowRepository; + } + public bool DeleteWorkflow(string name) + { + var workflow = _workflowRepository.FindOne(m => m.Name == name); + _workflowRepository.DeleteById(workflow.Id.ToString()); + return true; + } + + public async Task InsertWorkflow(SyntheticWorkflow workflow) + { + await _workflowRepository.InsertOneAsync(MongoMappers.Map(workflow)); + } + + public async Task UpdateWorkflow(SyntheticWorkflow workflow) + { + var w = await _workflowRepository.FindOneAsync(m => m.Name == workflow.Name); + var dto = MongoMappers.Map(workflow); + dto.Id = w.Id; + _workflowRepository.Update(dto); + } + + public async Task GetWorkflow(string name) + { + var workflow = await _workflowRepository.FindOneAsync(m => m.Name == name); + return MongoMappers.Map(workflow); + } + + public IEnumerable GetWorkflows() + { + var workflows = _workflowRepository.FindAll(); + return workflows.Select(m => MongoMappers.Map(m)); + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Abstractions/IExchangeProcessor.cs b/src/Managing.Infrastructure.Exchanges/Abstractions/IExchangeProcessor.cs new file mode 100644 index 0000000..1746891 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Abstractions/IExchangeProcessor.cs @@ -0,0 +1,39 @@ +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Abstractions; + +public interface IExchangeProcessor +{ + public Enums.TradingExchanges Exchange(); + void LoadClient(Account account); + Task 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 GetBalance(Account account, bool isForPaperTrading = false); + Task> GetBalances(Account account, bool isForPaperTrading = false); + decimal GetPrice(Account account, Ticker ticker, DateTime date); + Task GetTrade(Account account, string order, Ticker ticker); + Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval); + decimal GetVolume(Account account, Ticker ticker); + Task> GetTrades(Account account, Ticker ticker); + Task CancelOrder(Account account, Ticker ticker); + decimal GetFee(Account account, bool isForPaperTrading = false); + Candle GetCandle(Account account, Ticker ticker, DateTime date); + Task GetQuantityInPosition(Account account, Ticker ticker); + Orderbook GetOrderbook(Account account, Ticker ticker); + Task> GetOrders(Account account, Ticker ticker); + Task GetTrade(string reference, string orderId, Ticker ticker); +} diff --git a/src/Managing.Infrastructure.Exchanges/CandleHelpers.cs b/src/Managing.Infrastructure.Exchanges/CandleHelpers.cs new file mode 100644 index 0000000..d7f2b7d --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/CandleHelpers.cs @@ -0,0 +1,82 @@ +using Binance.Net.Interfaces; +using FTX.Net.Objects.Models; +using Kraken.Net.Objects.Models; +using Managing.Domain.Candles; +using Managing.Infrastructure.Databases.MongoDb.Collections; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges +{ + public static class CandleHelpers + { + public static CandleDto Convert(IBinanceKline candle, Ticker ticker, Timeframe timeframe) + { + return new CandleDto() + { + Ticker = ticker.ToString(), + Timeframe = timeframe, + BaseVolume = candle.Volume, + Close = candle.ClosePrice, + CloseTime = candle.CloseTime, + Open = candle.OpenPrice, + OpenTime = candle.OpenTime, + High = candle.HighPrice, + Low = candle.LowPrice, + QuoteVolume = candle.QuoteVolume, + TakerBuyBaseVolume = candle.TakerBuyBaseVolume, + TakerBuyQuoteVolume = candle.TakerBuyQuoteVolume, + TradeCount = candle.TradeCount, + }; + } + + public static CandleDto Convert(KrakenStreamKline candle, Ticker ticker, Timeframe timeframe) + { + return new CandleDto() + { + Ticker = ticker.ToString(), + Timeframe = timeframe, + BaseVolume = candle.Volume, + Close = candle.ClosePrice, + CloseTime = candle.CloseTime, + Open = candle.OpenPrice, + OpenTime = candle.OpenTime, + High = candle.HighPrice, + Low = candle.LowPrice, + QuoteVolume = candle.VolumeWeightedAveragePrice, + TradeCount = candle.TradeCount + }; + } + + internal static Candle Map(FTXKline lastCandle) + { + return new Candle + { + OpenTime = lastCandle.OpenTime, + High = lastCandle.HighPrice, + Low = lastCandle.LowPrice, + Close = lastCandle.ClosePrice, + Open = lastCandle.OpenPrice + }; + } + + internal static Candle Map(IBinanceStreamKline candle, Ticker ticker, Timeframe timeframe) + { + return new Candle() + { + Ticker = ticker.ToString(), + Timeframe = timeframe, + BaseVolume = candle.Volume, + Close = candle.ClosePrice, + Date = candle.CloseTime, + Open = candle.OpenPrice, + OpenTime = candle.OpenTime, + High = candle.HighPrice, + Low = candle.LowPrice, + QuoteVolume = candle.QuoteVolume, + TakerBuyBaseVolume = candle.TakerBuyBaseVolume, + TakerBuyQuoteVolume = candle.TakerBuyQuoteVolume, + TradeCount = candle.TradeCount, + }; + } + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Configurations/ExchangeConfiguration.cs b/src/Managing.Infrastructure.Exchanges/Configurations/ExchangeConfiguration.cs new file mode 100644 index 0000000..880c5a3 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Configurations/ExchangeConfiguration.cs @@ -0,0 +1,23 @@ +using CryptoExchange.Net.Authentication; +using Microsoft.Extensions.Configuration; + +namespace Managing.Infrastructure.Exchanges.Configurations +{ + public static class ExchangeConfiguration + { + public static ApiCredentials GetBinanceApiCredentials(this IConfiguration configuration) + { + return new ApiCredentials(configuration.GetValue("Credentials:Binance:Key"), configuration.GetValue("Credentials:Binance:Secret")); + } + + public static ApiCredentials GetKrakenApiCredentials(this IConfiguration configuration) + { + return new ApiCredentials(configuration.GetValue("Credentials:Kraken:Key"), configuration.GetValue("Credentials:Kraken:Secret")); + } + + public static ApiCredentials GetFtxApiCredentials(this IConfiguration configuration) + { + return new ApiCredentials(configuration.GetValue("Credentials:Ftx:Key"), configuration.GetValue("Credentials:Ftx:Secret")); + } + } +} diff --git a/src/Managing.Infrastructure.Exchanges/ExchangeService.cs b/src/Managing.Infrastructure.Exchanges/ExchangeService.cs new file mode 100644 index 0000000..d33e1c6 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/ExchangeService.cs @@ -0,0 +1,242 @@ +using Managing.Domain.Trades; +using Managing.Domain.Candles; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; +using Managing.Domain.Accounts; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Infrastructure.Exchanges.Abstractions; + +namespace Managing.Infrastructure.Exchanges +{ + public class ExchangeService : IExchangeService + { + private readonly ILogger _logger; + private readonly ICandleRepository _candleRepository; + private readonly IEnumerable _exchangeProcessor; + + public ExchangeService(ILogger logger, ICandleRepository candleRepository, IEnumerable processor) + { + _logger = logger; + _candleRepository = candleRepository; + _exchangeProcessor = processor; + } + + #region Trades + public async Task 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) + { + _logger.LogInformation($"OpenMarketTrade - {ticker} - Type: {tradeType} - {direction} - Price: {price} - Quantity: {quantity} - Leverage: {leverage}"); + + if (isForPaperTrading) + { + return BuildEmptyTrade(ticker, price, quantity, direction, leverage, tradeType, currentDate.Value, reduceOnly ? TradeStatus.PendingOpen : TradeStatus.Filled); + } + + var processor = GetProcessor(account); + return await processor.OpenTrade(account, ticker, direction, price, quantity, leverage, tradeType, reduceOnly, isForPaperTrading, currentDate, ioc); + } + + private IExchangeProcessor GetProcessor(Account account) + { + return _exchangeProcessor.First(e => e.Exchange() == account.Exchange); + } + + public Trade BuildEmptyTrade(Ticker ticker, decimal price, decimal quantity, TradeDirection direction, decimal? leverage, TradeType tradeType, + DateTime dateTime, TradeStatus tradeStatus) + { + return new Trade(dateTime, direction, tradeStatus, tradeType, ticker, quantity, price, leverage, + Guid.NewGuid().ToString(), "EmptyTrade"); + } + + public async Task OpenStopLoss(Account account, Ticker ticker, TradeDirection originalDirection, decimal stopLossPrice, + decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null) + { + return await OpenTrade( + account, + ticker, + originalDirection == TradeDirection.Long ? TradeDirection.Short : TradeDirection.Long, + stopLossPrice, + quantity, + tradeType: TradeType.StopLoss, + isForPaperTrading: isForPaperTrading, + currentDate: currentDate, + reduceOnly: true); + } + + public async Task OpenTakeProfit(Account account, Ticker ticker, TradeDirection originalDirection, decimal takeProfitPrice, + decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null) + { + return await OpenTrade( + account, + ticker, + originalDirection == TradeDirection.Long ? TradeDirection.Short : TradeDirection.Long, + takeProfitPrice, + quantity, + tradeType: TradeType.TakeProfit, + isForPaperTrading: isForPaperTrading, + currentDate: currentDate, + reduceOnly: true); + } + + public Task ClosePosition(Account account, Position position, decimal lastPrice, bool isForPaperTrading = false) + { + var direction = position.OriginDirection == TradeDirection.Long ? TradeDirection.Short : TradeDirection.Long; + + if (isForPaperTrading) + { + var fake = BuildEmptyTrade(position.Open.Ticker, + lastPrice, + position.Open.Quantity, + direction, + position.Open.Leverage, + TradeType.Market, + position.Open.Date, + TradeStatus.Filled); + return Task.FromResult(fake); + } + + var processor = GetProcessor(account); + var closedTrade = processor.OpenTrade( + account, + position.Ticker, + direction, + lastPrice, + position.Open.Quantity, + position.Open.Leverage, + tradeType: TradeType.Market, + reduceOnly: true).Result; + + if (account.Exchange != Common.Enums.TradingExchanges.Evm) + { + closedTrade.Price = processor.GetTrade(account, closedTrade.ExchangeOrderId, closedTrade.Ticker).Result.Price; + } + + return Task.FromResult(closedTrade); + } + #endregion + + + public async Task CancelOrder(Account account, Ticker ticker) + { + var processor = GetProcessor(account); + return await processor.CancelOrder(account, ticker); + } + + public async Task GetTrade(Account account, string orderId, Ticker ticker) + { + var processor = GetProcessor(account); + return await processor.GetTrade(account, orderId, ticker); + } + + public async Task GetTrade(string reference, string orderId, Ticker ticker) + { + var processor = _exchangeProcessor.First(e => e.Exchange() == Common.Enums.TradingExchanges.Evm); + return await processor.GetTrade(reference, orderId, ticker); + } + + public async Task> GetTrades(Account account, Ticker ticker) + { + var processor = GetProcessor(account); + return await processor.GetTrades(account, ticker); + + } + + public async Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe timeframe) + { + var processor = GetProcessor(account); + return await processor.GetCandles(account, ticker, startDate, timeframe); + } + + public async Task> GetCandlesInflux(TradingExchanges exchange, Ticker ticker, DateTime startDate, Timeframe timeframe) + { + var candlesFromRepo = await _candleRepository.GetCandles(exchange, ticker, timeframe, startDate); + return candlesFromRepo.ToList(); + } + + public async Task GetBalance(Account account, bool isForPaperTrading = false) + { + if (isForPaperTrading) + { + return 1000; + } + + var processor = GetProcessor(account); + return await processor.GetBalance(account); + } + + public decimal GetFee(Account account, bool isForPaperTrading = false) + { + var processor = GetProcessor(account); + return processor.GetFee(account); + } + + public decimal GetPrice(Account account, Ticker ticker, DateTime date) + { + var processor = GetProcessor(account); + return processor.GetPrice(account, ticker, date); + } + + public Candle GetCandle(Account account, Ticker ticker, DateTime date) + { + var processor = GetProcessor(account); + return processor.GetCandle(account, ticker, date); + } + + public async Task GetQuantityInPosition(Account account, Ticker ticker) + { + var processor = GetProcessor(account); + return await processor.GetQuantityInPosition(account, ticker); + } + + public decimal GetVolume(Account account, Ticker ticker) + { + var processor = GetProcessor(account); + return processor.GetVolume(account, ticker); + } + + public async Task> GetTickers(Account account, Timeframe timeframe) + { + var tickers = await _candleRepository.GetTickersAsync(account.Exchange, timeframe, DateTime.UtcNow.AddDays(-2)); + return tickers.ToList(); + } + + public decimal GetBestPrice(Account account, Ticker ticker, decimal lastPrice, decimal quantity, TradeDirection direction) + { + if (account.Exchange == Common.Enums.TradingExchanges.Evm) + { + return GetPrice(account, ticker, DateTime.UtcNow); + } + + return GetOrderbook(account, ticker).GetBestPrice(direction, quantity); + } + + public Orderbook GetOrderbook(Account account, Ticker ticker) + { + var processor = GetProcessor(account); + return processor.GetOrderbook(account, ticker); + } + + public async Task> GetBalances(Account account, bool isForPaperTrading = false) + { + var processor = GetProcessor(account); + return await processor.GetBalances(account, isForPaperTrading); + } + + public async Task> GetOpenOrders(Account account, Ticker ticker) + { + var processor = GetProcessor(account); + return await processor.GetOrders(account, ticker); + } + } +} diff --git a/src/Managing.Infrastructure.Exchanges/ExchangeStream.cs b/src/Managing.Infrastructure.Exchanges/ExchangeStream.cs new file mode 100644 index 0000000..eaf0690 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/ExchangeStream.cs @@ -0,0 +1,41 @@ +using Binance.Net.Interfaces.Clients; +using Managing.Application.Abstractions.Services; +using Managing.Application.Shared; +using Managing.Domain.Candles; +using Managing.Infrastructure.Exchanges.Helpers; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges; + +public class ExchangeStream : IExchangeStream +{ + private readonly ILogger logger; + private readonly IBinanceSocketClient _binanceSocketClient; + + public ExchangeStream(IBinanceSocketClient binanceSocketClient, ILogger logger) + { + _binanceSocketClient = binanceSocketClient; + this.logger = logger; + } + + public async Task StartBinanceWorker(Ticker ticker, Func action) + { + logger.LogInformation($"Starting binance worker for {ticker}"); + + await _binanceSocketClient.SpotApi.ExchangeData.SubscribeToKlineUpdatesAsync(BinanceHelpers.ToBinanceTicker(ticker), Binance.Net.Enums.KlineInterval.OneSecond, candle => + { + if (candle.Data.Data?.Final == true) + { + //action((candle) => { CandleHelpers.Map(candle.Data.Data, ticker, Timeframe.FiveMinutes)}); + action(CandleHelpers.Map(candle.Data.Data, ticker, Timeframe.FiveMinutes)); + } + }); + } + + public async Task StopBinanceWorker() + { + logger.LogInformation($"Stoping all Binance worker subscription"); + await _binanceSocketClient.UnsubscribeAllAsync(); + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/BaseProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/BaseProcessor.cs new file mode 100644 index 0000000..6403980 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Exchanges/BaseProcessor.cs @@ -0,0 +1,30 @@ +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using Managing.Infrastructure.Exchanges.Abstractions; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Exchanges +{ + public abstract class BaseProcessor : IExchangeProcessor + { + public abstract void LoadClient(Account account); + public abstract Task CancelOrder(Account account, Ticker ticker); + public abstract Enums.TradingExchanges Exchange(); + public abstract Task GetBalance(Account account, bool isForPaperTrading = false); + public abstract Candle GetCandle(Account account, Ticker ticker, DateTime date); + public abstract Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval); + public abstract decimal GetFee(Account account, bool isForPaperTrading = false); + public abstract decimal GetPrice(Account account, Ticker ticker, DateTime date); + public abstract Task GetQuantityInPosition(Account account, Ticker ticker); + public abstract Task GetTrade(Account account, string order, Ticker ticker); + public abstract Task> GetTrades(Account account, Ticker ticker); + public abstract decimal GetVolume(Account account, Ticker ticker); + public abstract Task OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = null, Enums.TradeType tradeType = Enums.TradeType.Limit, bool reduceOnly = false, bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true); + public abstract Orderbook GetOrderbook(Account account, Ticker ticker); + public abstract Task> GetBalances(Account account, bool isForPaperTrading = false); + public abstract Task> GetOrders(Account account, Ticker ticker); + public abstract Task GetTrade(string reference, string orderId, Ticker ticker); + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/BinanceProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/BinanceProcessor.cs new file mode 100644 index 0000000..4135ee7 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Exchanges/BinanceProcessor.cs @@ -0,0 +1,172 @@ +using Binance.Net.Clients; +using Binance.Net.Enums; +using Binance.Net.Interfaces.Clients; +using CryptoExchange.Net.Authentication; +using Managing.Common; +using Managing.Core; +using Managing.Domain.Accounts; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using Managing.Infrastructure.Exchanges.Helpers; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Exchanges; + +public class BinanceProcessor : BaseProcessor +{ + private ILogger _logger; + private IBinanceRestClient _binanceClient; + + public BinanceProcessor(ILogger logger) + { + _logger = logger; + } + + public override async Task CancelOrder(Account account, Ticker ticker) + { + var binanceResult = await _binanceClient.UsdFuturesApi.Trading.CancelAllOrdersAsync(BinanceHelpers.ToBinanceTicker(ticker)); + return binanceResult.Success; + } + + public override Enums.TradingExchanges Exchange() => Enums.TradingExchanges.Binance; + + public override async Task GetBalance(Account account, bool isForPaperTrading = false) + { + var balance = 0m; + var binanceBalance = await _binanceClient.UsdFuturesApi.Account.GetBalancesAsync(); + foreach (var item in binanceBalance.Data) + { + balance += item.AvailableBalance; + } + return balance; + } + + public override Task> GetBalances(Account account, bool isForPaperTrading = false) + { + throw new NotImplementedException(); + } + + public override Candle GetCandle(Account account, Ticker ticker, DateTime date) + { + throw new NotImplementedException(); + } + + public override async Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe timeframe) + { + var binanceCandles = await _binanceClient.UsdFuturesApi.ExchangeData.GetKlinesAsync(BinanceHelpers.ToBinanceTicker(ticker), + BinanceHelpers.Map(timeframe), startDate); + + return (List)binanceCandles.Data.Select(binanceKline => BinanceHelpers.Map(binanceKline, ticker, account.Exchange)); + } + + public override decimal GetFee(Account account, bool isForPaperTrading = false) + { + throw new NotImplementedException(); + } + + public override Orderbook GetOrderbook(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override Task> GetOrders(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override decimal GetPrice(Account account, Ticker ticker, DateTime date) + { + var binancePrice = _binanceClient.UsdFuturesApi.ExchangeData.GetPriceAsync(BinanceHelpers.ToBinanceTicker(ticker)).Result.Data; + return binancePrice.Price; + } + + public override async Task GetQuantityInPosition(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override async Task GetTrade(Account account, string order, Ticker ticker) + { + var binanceOrder = await _binanceClient.UsdFuturesApi.Trading.GetOrderAsync(BinanceHelpers.ToBinanceTicker(ticker), origClientOrderId: order); + return BinanceHelpers.Map(binanceOrder.Data); + } + + public override Task GetTrade(string reference, string orderId, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override async Task> GetTrades(Account account, Ticker ticker) + { + var binanceOrder = + await _binanceClient.UsdFuturesApi.Trading.GetOrdersAsync(BinanceHelpers.ToBinanceTicker(ticker)); + return (List)binanceOrder.Data.Select(o => BinanceHelpers.Map(o)); + } + + public override decimal GetVolume(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override void LoadClient(Account account) + { + var credentials = new ApiCredentials(account.Key, account.Secret); + _binanceClient = new BinanceRestClient((options) => { options.ApiCredentials = credentials; }); + } + + public override async Task 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) + { + var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price, leverage, "", ""); + trade.SetQuantity(quantity, GetQuantityPrecision(account, ticker)); + trade.SetPrice(price, GetPricePrecision(account, ticker)); + + if (trade.Quantity <= 0) + throw new InvalidOperationException( + $"Minimum quantity not match, cannot execute trade {direction} for {ticker}."); + + var binanceOrderType = BinanceHelpers.BinanceOrderTypeMap(tradeType); + var binanceResult = await _binanceClient.UsdFuturesApi.Trading.PlaceOrderAsync( + BinanceHelpers.ToBinanceTicker(ticker), + direction != TradeDirection.Long ? OrderSide.Sell : OrderSide.Buy, + binanceOrderType, + price: binanceOrderType == FuturesOrderType.Limit ? trade.Price : null, + quantity: trade.Quantity, + reduceOnly: reduceOnly, + priceProtect: true, + timeInForce: binanceOrderType == FuturesOrderType.Limit ? TimeInForce.GoodTillCanceled : null, + activationPrice: binanceOrderType == FuturesOrderType.Limit ? trade.Price : null, + stopPrice: binanceOrderType == FuturesOrderType.StopMarket ? trade.Price : null); + + _logger.LogInformation("Exchange result : {0}", JsonConvert.SerializeObject(binanceResult)); + + var binanceOrderExecuted = BinanceHelpers.Map(binanceResult, leverage); + + if (binanceResult.Success) + { + var binanceOrder = await GetTrade(account, binanceOrderExecuted.ExchangeOrderId, ticker); + + trade.Price = binanceOrder.Price; + trade.SetStatus(binanceOrder.Status); + trade.SetExchangeOrderId(binanceOrderExecuted.ExchangeOrderId); + trade.SetMessage(binanceOrderExecuted.Message); + } + return trade; + } + + private int GetPricePrecision(Account account, Ticker ticker) + { + var binanceFutureInfo = _binanceClient.UsdFuturesApi.ExchangeData.GetExchangeInfoAsync().Result.Data; + var precision = binanceFutureInfo.Symbols.Single(p => p.Name == BinanceHelpers.ToBinanceTicker(ticker)) + .PriceFilter.TickSize; + return MathHelpers.GetDecimalPlaces(precision); + } + + private int GetQuantityPrecision(Account account, Ticker ticker) + { + var binanceFutureInfo = _binanceClient.UsdFuturesApi.ExchangeData.GetExchangeInfoAsync().Result.Data; + var precision = binanceFutureInfo.Symbols.Single(p => p.Name == BinanceHelpers.ToBinanceTicker(ticker)).QuantityPrecision; + return Convert.ToInt32(precision); + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/EvmProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/EvmProcessor.cs new file mode 100644 index 0000000..8f2a4a5 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Exchanges/EvmProcessor.cs @@ -0,0 +1,172 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Candles; +using Managing.Domain.Evm; +using Managing.Domain.Trades; +using Microsoft.Extensions.Logging; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Exchanges; + +public class EvmProcessor : BaseProcessor +{ + public override Enums.TradingExchanges Exchange() => Enums.TradingExchanges.Evm; + + private ILogger _logger; + private IEvmManager _evmManager; + + public EvmProcessor(ILogger logger, IEvmManager evmManager) + { + _logger = logger; + _evmManager = evmManager; + } + + public override async Task CancelOrder(Account account, Ticker ticker) + { + return await _evmManager.CancelOrders(account, ticker); + } + + + public override async Task GetBalance(Account account, bool isForPaperTrading = false) + { + //var balances = await _evmManager.GetAllBalancesOnAllChain(account.Key); + //var balanceAmount = 0m; + + //foreach (var balance in balances) + //{ + // balanceAmount += balance.Value; + //} + + var evmBalance = await _evmManager.GetTokenBalance(Constants.Chains.Arbitrum, Ticker.USDC, account.Key); + return evmBalance.Balance; + } + + public override async Task> GetBalances(Account account, bool isForPaperTrading = false) + { + var balances = await _evmManager.GetAllBalancesOnAllChain(account.Key); + return Map(balances); + } + + private List Map(List balances) + { + return balances.ConvertAll(balance => new Balance + { + TokenName = balance.TokenName, + Price = balance.Price, + Value = balance.Value, + Amount = balance.Balance, + TokenAdress = balance.TokenAddress, + Chain = balance.Chain + }); + } + + public override Candle GetCandle(Account account, Ticker ticker, DateTime date) + { + return _evmManager.GetCandle(SubgraphProvider.Gbc, ticker).Result; + } + + public override async Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval) + { + return await _evmManager.GetCandles(SubgraphProvider.Gbc, ticker, startDate, interval); + } + + public override decimal GetFee(Account account, bool isForPaperTrading = false) + { + return _evmManager.GetFee(Constants.Chains.Arbitrum).Result; + } + + public override decimal GetPrice(Account account, Ticker ticker, DateTime date) + { + return GetCandle(account, ticker, date).Close; + } + + public override async Task GetQuantityInPosition(Account account, Ticker ticker) + { + return await _evmManager.QuantityInPosition(Constants.Chains.Arbitrum, account.Key, ticker); + } + + public override async Task GetTrade(Account account, string order, Ticker ticker) + { + return await _evmManager.GetTrade(account, Constants.Chains.Arbitrum, ticker); + } + + public override async Task GetTrade(string reference, string orderId, Ticker ticker) + { + return await _evmManager.GetTrade(reference, Constants.Chains.Arbitrum, ticker); + } + + public override decimal GetVolume(Account account, Ticker ticker) + { + var volume = _evmManager.GetVolume(SubgraphProvider.ChainlinkPrice, ticker); + return volume; + } + + public override async Task 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) + { + Trade trade; + if (reduceOnly) + { + if (tradeType is TradeType.TakeProfit + or TradeType.StopLoss) + { + // If trade type is TP or SL we create DecreaseOrder + trade = await _evmManager.DecreaseOrder(account, tradeType, ticker, direction, price, quantity, leverage); + } + else + { + // Trade requested is not an SL nor TP + // Price is the current price + // We create Decrease position + trade = await _evmManager.DecreasePosition(account, ticker, direction, price, quantity, leverage); + } + } + else + { + trade = await _evmManager.IncreasePosition(account, ticker, direction, price, quantity, leverage); + } + + return trade; + } + + public override async Task> GetOrders(Account account, Ticker ticker) + { + return await _evmManager.GetOrders(account, ticker); + + } + + + + #region Not implemented + + public override void LoadClient(Account account) + { + // No client needed + throw new NotImplementedException(); + } + + public override async Task> GetTrades(Account account, Ticker ticker) + { + // Use by commandHandler to get trades list + throw new NotImplementedException(); + } + + public override Orderbook GetOrderbook(Account account, Ticker ticker) + { + // Not use because EVM do not based on an orderbook + throw new NotImplementedException(); + } + + #endregion +} diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/FtxProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/FtxProcessor.cs new file mode 100644 index 0000000..634d59e --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Exchanges/FtxProcessor.cs @@ -0,0 +1,199 @@ +using CryptoExchange.Net.Authentication; +using FTX.Net.Clients; +using FTX.Net.Interfaces.Clients; +using FTX.Net.Objects; +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using Managing.Infrastructure.Exchanges.Helpers; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Exchanges; + +public class FtxProcessor : BaseProcessor +{ + private IFTXClient _ftxClient; + private ILogger _logger; + + public FtxProcessor(ILogger logger) + { + _logger = logger; + } + + public override Enums.TradingExchanges Exchange() => Enums.TradingExchanges.Ftx; + + public override void LoadClient(Account account) + { + var credentials = new ApiCredentials(account.Key, account.Secret); + + var ftxConfig = new FTXClientOptions() + { + SubaccountName = account.Name + }; + _ftxClient = new FTXClient(ftxConfig); + } + + + public override async Task CancelOrder(Account account, Ticker ticker) + { + LoadClient(account); + var ftxResult = await _ftxClient.TradeApi.Trading.CancelAllOrdersAsync(FtxHelpers.ToFtxTicker(ticker)); + return ftxResult.Success; + } + + public override async Task GetBalance(Account account, bool isForPaperTrading = false) + { + LoadClient(account); + + var balance = 0m; + var ftxBalance = await _ftxClient.TradeApi.Account.GetBalancesAsync(); + foreach (var item in ftxBalance.Data) + { + balance += item.UsdValue; + } + return balance; + } + + public override Candle GetCandle(Account account, Ticker ticker, DateTime date) + { + LoadClient(account); + + var ftxKlines = _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker), + FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data; + if (ftxKlines != null && ftxKlines.Any()) + { + var lastCandle = ftxKlines.ToList().LastOrDefault(); + return CandleHelpers.Map(lastCandle); + } + + return null; + } + + public override async Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe timeframe) + { + LoadClient(account); + + var candles = new List(); + var ftxCandles = await _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker), + FtxHelpers.Map(timeframe), startDate); + + if (ftxCandles.Success) + candles.AddRange(ftxCandles.Data.SkipLast(1).Select(ftxKline => FtxHelpers.Map(ftxKline, ticker, account.Exchange, timeframe))); + + return candles; + } + + public override decimal GetFee(Account account, bool isForPaperTrading = false) + { + LoadClient(account); + var ftxResult = _ftxClient.TradeApi.Account.GetAccountInfoAsync().Result.Data; + return ftxResult.TakerFee; + } + + public override decimal GetPrice(Account account, Ticker ticker, DateTime date) + { + LoadClient(account); + var ftxKlines = _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker), + FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data; + if (ftxKlines != null && ftxKlines.Any()) + { + return ftxKlines.ToList().LastOrDefault().ClosePrice; + } + return 0; + } + + public override async Task GetQuantityInPosition(Account account, Ticker ticker) + { + var ftxTickerBalance = _ftxClient.TradeApi.Account.GetPositionsAsync().Result.Data; + return ftxTickerBalance.FirstOrDefault(f => f.Future == FtxHelpers.ToFtxTicker(ticker)).Quantity; + } + + public override async Task GetTrade(Account account, string order, Ticker ticker) + { + LoadClient(account); + var ftxOrder = await _ftxClient.TradeApi.Trading.GetOrderByClientOrderIdAsync(order); + return FtxHelpers.Map(ftxOrder.Data); + } + + public override async Task> GetTrades(Account account, Ticker ticker) + { + LoadClient(account); + var ftxOrder = await _ftxClient.TradeApi.Trading.GetOrdersAsync(ticker.ToString()); + return (List)ftxOrder.Data.Select(o => FtxHelpers.Map(o)); + } + + public override decimal GetVolume(Account account, Ticker ticker) + { + var futureStats = _ftxClient.TradeApi.ExchangeData.GetFutureStatsAsync(FtxHelpers.ToFtxTicker(ticker)).Result.Data; + return futureStats.Volume; + } + + public override async Task 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) + { + LoadClient(account); + + var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price, leverage, "", ""); + + trade.SetQuantity(quantity, 6); + + Trade ftxOrder; + if (tradeType == TradeType.StopLoss || tradeType == TradeType.TakeProfitLimit || tradeType == TradeType.StopMarket) + { + var ftxTriggerOrderType = FtxHelpers.FtxTriggerOrderTypeMap(tradeType); + var ftxResult = await _ftxClient.TradeApi.Trading.PlaceTriggerOrderAsync(FtxHelpers.ToFtxTicker(ticker), + direction != TradeDirection.Long ? FTX.Net.Enums.OrderSide.Sell : FTX.Net.Enums.OrderSide.Buy, + ftxTriggerOrderType, + triggerPrice: price, + reduceOnly: true, + retryUntilFilled: false, + quantity: quantity); + _logger.LogInformation("Exchange result : {0}", JsonConvert.SerializeObject(ftxResult)); + ftxOrder = FtxHelpers.Map(ftxResult, leverage); + } + else + { + var ftxOrderType = FtxHelpers.FtxOrderTypeMap(tradeType); + var ftxResult = await _ftxClient.TradeApi.Trading.PlaceOrderAsync(FtxHelpers.ToFtxTicker(ticker), + direction != TradeDirection.Long ? FTX.Net.Enums.OrderSide.Sell : FTX.Net.Enums.OrderSide.Buy, + ftxOrderType, + price: ftxOrderType == FTX.Net.Enums.OrderType.Limit ? price : null, + quantity: quantity, + clientOrderId: Guid.NewGuid().ToString(), + immediateOrCancel: ioc); + _logger.LogInformation("Exchange result : {0}", JsonConvert.SerializeObject(ftxResult)); + ftxOrder = FtxHelpers.Map(ftxResult, leverage); + } + + trade.SetStatus(ftxOrder.Status); + trade.SetExchangeOrderId(ftxOrder.ExchangeOrderId); + trade.SetMessage(ftxOrder.Message); + trade.Price = ftxOrder.Price; + + return trade; + } + + public override Orderbook GetOrderbook(Account account, Ticker ticker) + { + LoadClient(account); + var ftxOrderBook = _ftxClient.TradeApi.ExchangeData.GetOrderBookAsync(FtxHelpers.ToFtxTicker(ticker), 100).Result; + return FtxHelpers.Map(ftxOrderBook); + } + + public override Task> GetBalances(Account account, bool isForPaperTrading = false) + { + throw new NotImplementedException(); + } + + public override Task> GetOrders(Account acount, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override Task GetTrade(string reference, string orderId, Ticker ticker) + { + throw new NotImplementedException(); + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/KrakenProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/KrakenProcessor.cs new file mode 100644 index 0000000..4309a44 --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Exchanges/KrakenProcessor.cs @@ -0,0 +1,138 @@ +using CryptoExchange.Net.Authentication; +using Kraken.Net.Clients; +using Kraken.Net.Interfaces.Clients; +using Kraken.Net.Objects.Options; +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using Managing.Infrastructure.Exchanges.Helpers; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Exchanges; + +public class KrakenProcessor : BaseProcessor +{ + private ILogger _logger; + private IKrakenRestClient _krakenClient; + + public KrakenProcessor(ILogger logger) + { + _logger = logger; + } + public override Task CancelOrder(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override Enums.TradingExchanges Exchange() => Enums.TradingExchanges.Kraken; + + public override async Task GetBalance(Account account, bool isForPaperTrading = false) + { + LoadClient(account); + var balance = await _krakenClient.SpotApi.Account.GetBalancesAsync(); + balance.Data.TryGetValue("USDT", out decimal krakenBalance); + return krakenBalance; + } + + public override Task> GetBalances(Account account, bool isForPaperTrading = false) + { + throw new NotImplementedException(); + } + + public override Candle GetCandle(Account account, Ticker ticker, DateTime date) + { + throw new NotImplementedException(); + } + + public override Task> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval) + { + throw new NotImplementedException(); + } + + public override decimal GetFee(Account account, bool isForPaperTrading = false) + { + throw new NotImplementedException(); + } + + public override Orderbook GetOrderbook(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override Task> GetOrders(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override decimal GetPrice(Account account, Ticker ticker, DateTime date) + { + LoadClient(account); + var krakenKline = _krakenClient.SpotApi.ExchangeData.GetKlinesAsync(ticker.ToString(), + Kraken.Net.Enums.KlineInterval.OneMinute, date).Result.Data.Data.ToList()[0]; + return (krakenKline.HighPrice + krakenKline.ClosePrice) / 2; + } + + public override Task GetQuantityInPosition(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override async Task GetTrade(Account account, string order, Ticker ticker) + { + LoadClient(account); + var krakenOrder = await _krakenClient.SpotApi.Trading.GetOrderAsync(order); + return KrakenHelpers.Map(krakenOrder.Data.Values.First()); + } + + public override Task GetTrade(string reference, string orderId, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override async Task> GetTrades(Account account, Ticker ticker) + { + LoadClient(account); + var krakenOrder = await _krakenClient.SpotApi.Trading.GetOrdersAsync(); + return (List)krakenOrder.Data.Select(o => KrakenHelpers.Map(o)); + } + + public override decimal GetVolume(Account account, Ticker ticker) + { + throw new NotImplementedException(); + } + + public override void LoadClient(Account account) + { + var credentials = new ApiCredentials(account.Key, account.Secret); + var krakenConfig = new KrakenRestOptions() + { + ApiCredentials = credentials + }; + _krakenClient = new KrakenRestClient((options) => { options.ApiCredentials = krakenConfig.ApiCredentials; }); + } + + public override async Task 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) + { + LoadClient(account); + var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price, leverage, "", ""); + trade.SetQuantity(quantity, 6); + trade.SetPrice(price, 1); + + var order = await _krakenClient.SpotApi.Trading.PlaceOrderAsync(ticker.ToString(), + direction != TradeDirection.Long ? Kraken.Net.Enums.OrderSide.Sell : Kraken.Net.Enums.OrderSide.Buy, + KrakenHelpers.KrakenOrderTypeMap(tradeType), + price: price, + quantity: quantity, + leverage: leverage); + + _logger.LogInformation("Exchange result : {0}", JsonConvert.SerializeObject(order)); + var krakenOrderExecuted = GetTrade(account, ((string[])order.Data.OrderIds)[0], ticker).Result; + trade.SetStatus(krakenOrderExecuted.Status); + trade.SetExchangeOrderId(krakenOrderExecuted.ExchangeOrderId); + trade.SetMessage(krakenOrderExecuted.Message); + return trade; + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Helpers/BinanceHelpers.cs b/src/Managing.Infrastructure.Exchanges/Helpers/BinanceHelpers.cs new file mode 100644 index 0000000..ea5aeeb --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Helpers/BinanceHelpers.cs @@ -0,0 +1,195 @@ +using Binance.Net.Enums; +using Binance.Net.Interfaces; +using Binance.Net.Objects.Models.Futures; +using CryptoExchange.Net.Objects; +using Managing.Common; +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Helpers +{ + public static class BinanceHelpers + { + public static Trade Map(BinanceFuturesOrder data) + { + if (data == null) + return null; + + return new Trade(data.CreateTime, + (data.Side == OrderSide.Buy) ? TradeDirection.Long : TradeDirection.Short, + (TradeStatus)data.Status, (TradeType)data.OriginalType, MiscExtensions.ParseEnum(data.Symbol), data.Quantity, data.AvgPrice, 1, + data.ClientOrderId, ""); + } + + public static FuturesOrderType BinanceOrderTypeMap(TradeType tradeType) + { + switch (tradeType) + { + case TradeType.Limit: + return FuturesOrderType.Limit; + case TradeType.Market: + return FuturesOrderType.Market; + case TradeType.StopMarket: + return FuturesOrderType.StopMarket; + case TradeType.StopLossLimit: + return FuturesOrderType.Stop; + default: + return FuturesOrderType.Limit; + } + } + + public static TradeType BinanceOrderTradeType(FuturesOrderType orderType) + { + switch (orderType) + { + case FuturesOrderType.Stop: + case FuturesOrderType.Limit: + return TradeType.Limit; + case FuturesOrderType.Market: + return TradeType.Market; + case FuturesOrderType.StopMarket: + return TradeType.StopMarket; + default: + return TradeType.Limit; + } + } + + public static Trade Map(WebCallResult result, decimal? leverage = null) + { + var data = result.Data; + + if (data == null) + { + return new Trade(DateTime.Now, TradeDirection.None, + TradeStatus.Cancelled, TradeType.Market, Ticker.BTC, 0, 0, 0, + "", result.Error?.Message); + } + + return new Trade(data.UpdateTime, + (data.PositionSide == PositionSide.Long) ? TradeDirection.Long : TradeDirection.Short, + (TradeStatus)data.Status, (TradeType)data.OriginalType, MiscExtensions.ParseEnum(data.Symbol), data.Quantity, data.Price, leverage, + data.ClientOrderId, ""); + } + + public static Candle Map(IBinanceKline binanceKline, Ticker ticker, Enums.TradingExchanges exchange) + { + return new Candle + { + Date = binanceKline.CloseTime, + BaseVolume = binanceKline.Volume, + Close = binanceKline.ClosePrice, + High = binanceKline.HighPrice, + Low = binanceKline.LowPrice, + Open = binanceKline.OpenPrice, + Ticker = ticker.ToString(), + QuoteVolume = binanceKline.QuoteVolume, + TradeCount = binanceKline.TradeCount, + OpenTime = binanceKline.OpenTime, + TakerBuyBaseVolume = binanceKline.TakerBuyBaseVolume, + TakerBuyQuoteVolume = binanceKline.TakerBuyQuoteVolume, + Exchange = exchange + }; + } + + internal static KlineInterval Map(Timeframe interval) => interval switch + { + Timeframe.FiveMinutes => KlineInterval.FiveMinutes, + Timeframe.FifteenMinutes => KlineInterval.FifteenMinutes, + Timeframe.ThirtyMinutes => KlineInterval.ThirtyMinutes, + Timeframe.OneHour => KlineInterval.OneHour, + Timeframe.FourHour => KlineInterval.FourHour, + Timeframe.OneDay => KlineInterval.OneDay, + _ => throw new NotImplementedException(), + }; + + public static string ToBinanceTicker(Ticker ticker) + { + switch (ticker) + { + case Ticker.ADA: + return "ADAUSDT"; + case Ticker.ALGO: + return "ALGOUSDT"; + case Ticker.ATOM: + return "ATOMUSDT"; + case Ticker.AVAX: + return "AVAXUSDT"; + case Ticker.BAT: + return "BATUSDT"; + case Ticker.BNB: + return "BNBUSDT"; + case Ticker.BTC: + return "BTCUSDT"; + case Ticker.CRV: + return "CRVUSDT"; + case Ticker.DEFI: + return "DEFIUSDT"; + case Ticker.DOGE: + return "DOGEUSDT"; + case Ticker.DOT: + return "DOTUSDT"; + case Ticker.DYDX: + return "DYDXUSDT"; + case Ticker.ETC: + return "ETCUSDT"; + case Ticker.ETH: + return "ETHUSDT"; + case Ticker.FTM: + return "FTMUSDT"; + case Ticker.GALA: + return "GALAUSDT"; + case Ticker.GRT: + return "GRTUSDT"; + case Ticker.IMX: + return "IMXUSDT"; + case Ticker.KAVA: + return "KAVAUSDT"; + case Ticker.KSM: + return "KSMUSDT"; + case Ticker.LINK: + return "LINKUSDT"; + case Ticker.LRC: + return "LRCUSDT"; + case Ticker.LTC: + return "LTCUSDT"; + case Ticker.MATIC: + return "MATICUSDT"; + case Ticker.MKR: + return "MKRUSDT"; + case Ticker.NEAR: + return "NEARUSDT"; + case Ticker.NEO: + return "NEOUSDT"; + case Ticker.ONT: + return "ONTUSDT"; + case Ticker.SAND: + return "SANDUSDT"; + case Ticker.SOL: + return "SOLUSDT"; + case Ticker.SRM: + return "SRMUSDT"; + case Ticker.SUSHI: + return "SUSHIUSDT"; + case Ticker.THETA: + return "THETAUSDT"; + case Ticker.UNI: + return "UNIUSDT"; + case Ticker.WAVES: + return "WAVESUSDT"; + case Ticker.XMR: + return "XMRUSDT"; + case Ticker.XRP: + return "XRPUSDT"; + case Ticker.XTZ: + return "XTZUSDT"; + case Ticker.ZEC: + return "ZECUSDT"; + default: + break; + } + throw new NotImplementedException(); + } + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Helpers/FtxHelpers.cs b/src/Managing.Infrastructure.Exchanges/Helpers/FtxHelpers.cs new file mode 100644 index 0000000..3fcfe1c --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Helpers/FtxHelpers.cs @@ -0,0 +1,286 @@ +using CryptoExchange.Net.Objects; +using FTX.Net.Enums; +using FTX.Net.Objects.Models; +using Managing.Common; +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Helpers +{ + public static class FtxHelpers + { + public static string ToFtxTicker(Ticker ticker) + { + switch (ticker) + { + case Ticker.ADA: + return "ADA-PERP"; + case Ticker.APE: + return "APE-PERP"; + case Ticker.ALICE: + return "ALICE-PERP"; + case Ticker.ALGO: + return "ALGO-PERP"; + case Ticker.ATOM: + return "ATOM-PERP"; + case Ticker.AVAX: + return "AVAX-PERP"; + case Ticker.AXS: + return "AXS-PERP"; + case Ticker.BAT: + return "BAT-PERP"; + case Ticker.BNB: + return "BNB-PERP"; + case Ticker.BTC: + return "BTC-PERP"; + case Ticker.BAL: + return "BAL-PERP"; + case Ticker.C98: + return "C98-PERP"; + case Ticker.CHR: + return "CHR-PERP"; + case Ticker.CHZ: + return "CHZ-PERP"; + case Ticker.COMP: + return "COMP-PERP"; + case Ticker.CRO: + return "CRO-PERP"; + case Ticker.CRV: + return "CRV-PERP"; + case Ticker.CVC: + return "CVC-PERP"; + case Ticker.DEFI: + return "DEFI-PERP"; + case Ticker.DOGE: + return "DOGE-PERP"; + case Ticker.DOT: + return "DOT-PERP"; + case Ticker.DYDX: + return "DYDX-PERP"; + case Ticker.ENS: + return "ENS-PERP"; + case Ticker.ETC: + return "ETC-PERP"; + case Ticker.ETH: + return "ETH-PERP"; + case Ticker.FIL: + return "FIL-PERP"; + case Ticker.FLM: + return "FLM-PERP"; + case Ticker.FTM: + return "FTM-PERP"; + case Ticker.GALA: + return "GALA-PERP"; + case Ticker.GMT: + return "GMT-PERP"; + case Ticker.GRT: + return "GRT-PERP"; + case Ticker.HNT: + return "HNT-PERP"; + case Ticker.IMX: + return "IMX-PERP"; + case Ticker.JASMY: + return "JASMY-PERP"; + case Ticker.KAVA: + return "KAVA-PERP"; + case Ticker.KSM: + return "KSM-PERP"; + case Ticker.LDO: + return "LDO-PERP"; + case Ticker.LINK: + return "LINK-PERP"; + case Ticker.LOOKS: + return "LOOKS-PERP"; + case Ticker.LRC: + return "LRC-PERP"; + case Ticker.LTC: + return "LTC-PERP"; + case Ticker.MANA: + return "MANA-PERP"; + case Ticker.MATIC: + return "MATIC-PERP"; + case Ticker.MKR: + return "MKR-PERP"; + case Ticker.NEAR: + return "NEAR-PERP"; + case Ticker.NEO: + return "NEO-PERP"; + case Ticker.OMG: + return "OMG-PERP"; + case Ticker.ONE: + return "ONE-PERP"; + case Ticker.ONT: + return "ONT-PERP"; + case Ticker.QTUM: + return "QTUM-PERP"; + case Ticker.REEF: + return "REEF-PERP"; + case Ticker.REN: + return "REN-PERP"; + case Ticker.ROSE: + return "ROSE-PERP"; + case Ticker.RSR: + return "RSR-PERP"; + case Ticker.RUNE: + return "RUNE-PERP"; + case Ticker.SAND: + return "SAND-PERP"; + case Ticker.SOL: + return "SOL-PERP"; + case Ticker.SRM: + return "SRM-PERP"; + case Ticker.STMX: + return "STMX-PERP"; + case Ticker.SUSHI: + return "SUSHI-PERP"; + case Ticker.SXP: + return "SXP-PERP"; + case Ticker.THETA: + return "THETA-PERP"; + case Ticker.UNI: + return "UNI-PERP"; + case Ticker.VET: + return "VET-PERP"; + case Ticker.WAVES: + return "WAVES-PERP"; + case Ticker.XMR: + return "XMR-PERP"; + case Ticker.XRP: + return "XRP-PERP"; + case Ticker.XTZ: + return "XTZ-PERP"; + case Ticker.YFI: + return "YFI-PERP"; + case Ticker.ZEC: + return "ZEC-PERP"; + case Ticker.ZIL: + return "ZIL-PERP"; + default: + break; + } + throw new NotImplementedException(); + } + + + public static Trade Map(WebCallResult result, decimal? leverage = null) + { + var data = result.Data; + + if (data == null) + { + return new Trade(DateTime.Now, TradeDirection.None, + TradeStatus.Cancelled, TradeType.Market, Ticker.BTC, 0, 0, 0, + "", result.Error?.Message); + } + + return new Trade(data.CreateTime, + (data.Side == OrderSide.Buy) ? TradeDirection.Long : TradeDirection.Short, + (TradeStatus)data.Status, (TradeType)data.Type, MiscExtensions.ParseEnum(data.Symbol), data.Quantity, data.AverageFillPrice ?? 0, leverage, + data.ClientOrderId, ""); + } + + internal static Trade Map(WebCallResult ftxResult, decimal? leverage) + { + var data = ftxResult.Data; + + if (data == null) + { + return new Trade(DateTime.Now, TradeDirection.None, + TradeStatus.Cancelled, TradeType.Market, Ticker.BTC, 0, 0, 0, + "", ftxResult.Error?.Message); + } + + return new Trade(data.CreateTime, + (data.Side == OrderSide.Buy) ? TradeDirection.Long : TradeDirection.Short, + (TradeStatus)data.Status, (TradeType)data.Type, MiscExtensions.ParseEnum(data.Symbol), data.Quantity, data.TriggerPrice ?? 0, leverage, + Guid.NewGuid().ToString(), ""); + } + + public static OrderType FtxOrderTypeMap(TradeType tradeType) + { + switch (tradeType) + { + case TradeType.Limit: + return OrderType.Limit; + case TradeType.Market: + return OrderType.Market; + default: + return OrderType.Limit; + } + } + + public static Trade Map(FTXOrder data) + { + if (data == null) + return null; + + return new Trade(data.CreateTime, TradeDirection.None, + (TradeStatus)data.Status, (TradeType)data.Type, MiscExtensions.ParseEnum(data.Symbol), data.Quantity, data.AverageFillPrice ?? 0, 0, + data.ClientOrderId, ""); + } + + public static Candle Map( + FTXKline ftxKline, + Ticker ticker, + Enums.TradingExchanges exchange, + Timeframe timeframe) + { + return new Candle + { + Date = ftxKline.OpenTime, + BaseVolume = ftxKline.Volume ?? 0, + Close = ftxKline.ClosePrice, + High = ftxKline.HighPrice, + Low = ftxKline.LowPrice, + Open = ftxKline.OpenPrice, + Ticker = ticker.ToString(), + OpenTime = ftxKline.OpenTime, + Exchange = exchange, + Timeframe = timeframe + }; + } + + internal static KlineInterval Map(Timeframe interval) => interval switch + { + Timeframe.FiveMinutes => KlineInterval.FiveMinutes, + Timeframe.FifteenMinutes => KlineInterval.FifteenMinutes, + Timeframe.OneHour => KlineInterval.OneHour, + Timeframe.FourHour => KlineInterval.FourHours, + Timeframe.OneDay => KlineInterval.OneDay, + _ => throw new NotImplementedException(), + }; + + internal static TriggerOrderType FtxTriggerOrderTypeMap(TradeType tradeType) => tradeType switch + { + TradeType.StopMarket => TriggerOrderType.Stop, + TradeType.StopLimit => TriggerOrderType.Stop, + TradeType.StopLoss => TriggerOrderType.Stop, + TradeType.TakeProfit => TriggerOrderType.TakeProfit, + TradeType.StopLossProfit => TriggerOrderType.Stop, + TradeType.StopLossProfitLimit => TriggerOrderType.Stop, + TradeType.StopLossLimit => TriggerOrderType.Stop, + TradeType.TakeProfitLimit => TriggerOrderType.TakeProfit, + TradeType.TrailingStop => TriggerOrderType.TrailingStop, + TradeType.TrailingStopLimit => TriggerOrderType.TrailingStop, + TradeType.StopLossAndLimit => TriggerOrderType.Stop, + TradeType.SettlePosition => TriggerOrderType.Stop, + _ => throw new NotImplementedException(), + }; + + internal static Orderbook Map(WebCallResult ftxOrderBook) + { + return new Orderbook() + { + Asks = Map(ftxOrderBook.Data.Asks), + Bids = Map(ftxOrderBook.Data.Bids) + }; + } + + private static List Map(IEnumerable entry) + { + return entry.Select(ask => new OrderBookEntry() { Price = ask.Price, Quantity = ask.Quantity}).ToList(); + } + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Helpers/KrakenHelpers.cs b/src/Managing.Infrastructure.Exchanges/Helpers/KrakenHelpers.cs new file mode 100644 index 0000000..3abbb2e --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Helpers/KrakenHelpers.cs @@ -0,0 +1,46 @@ +using Kraken.Net.Objects.Models; +using Managing.Core; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Exchanges.Helpers; + +public static class KrakenHelpers +{ + public static Trade Map(KrakenOrder order) + { + var leverageParse = order.OrderDetails.Leverage.Split((char)':')[0]; + long.TryParse(leverageParse, out long leverage); + + return new Trade(order.CreateTime, + TradeDirection.None, + (TradeStatus)order.Status, + (TradeType)order.OrderDetails.Type, + MiscExtensions.ParseEnum(order.OrderDetails.Symbol), + order.Quantity, + order.AveragePrice, + leverage, + order.ClientOrderId, + ""); + } + + public static Kraken.Net.Enums.OrderType KrakenOrderTypeMap(TradeType tradeType) + { + switch (tradeType) + { + case TradeType.Limit: + return Kraken.Net.Enums.OrderType.Limit; + case TradeType.Market: + return Kraken.Net.Enums.OrderType.Market; + case TradeType.StopMarket: + return Kraken.Net.Enums.OrderType.StopMarket; + default: + return Kraken.Net.Enums.OrderType.Limit; + } + } + + internal static Trade Map(KeyValuePair o) + { + throw new NotImplementedException(); + } +} diff --git a/src/Managing.Infrastructure.Exchanges/Managing.Infrastructure.Exchanges.csproj b/src/Managing.Infrastructure.Exchanges/Managing.Infrastructure.Exchanges.csproj new file mode 100644 index 0000000..936d0fe --- /dev/null +++ b/src/Managing.Infrastructure.Exchanges/Managing.Infrastructure.Exchanges.csproj @@ -0,0 +1,23 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + + + + + + + + + + + diff --git a/src/Managing.Infrastructure.Messengers/Discord/CommandHandler.cs b/src/Managing.Infrastructure.Messengers/Discord/CommandHandler.cs new file mode 100644 index 0000000..ae85208 --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Discord/CommandHandler.cs @@ -0,0 +1,142 @@ +using Discord; +using Discord.Interactions; +using Discord.WebSocket; +using System.Reflection; + +namespace Managing.Infrastructure.Messengers.Discord +{ + public class CommandHandler + { + private readonly DiscordSocketClient _client; + private readonly InteractionService _commands; + private readonly IServiceProvider _services; + + public CommandHandler(DiscordSocketClient client, InteractionService commands, IServiceProvider services) + { + _client = client; + _commands = commands; + _services = services; + } + + public async Task InitializeAsync() + { + // add the public modules that inherit InteractionModuleBase to the InteractionService + await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); + + // process the InteractionCreated payloads to execute Interactions commands + _client.InteractionCreated += HandleInteraction; + + // process the command execution results + _commands.SlashCommandExecuted += SlashCommandExecuted; + _commands.ContextCommandExecuted += ContextCommandExecuted; + _commands.ComponentCommandExecuted += ComponentCommandExecuted; + } + + private Task ComponentCommandExecuted(ComponentCommandInfo arg1, IInteractionContext arg2, IResult arg3) + { + if (!arg3.IsSuccess) + { + switch (arg3.Error) + { + case InteractionCommandError.UnmetPrecondition: + // implement + break; + case InteractionCommandError.UnknownCommand: + // implement + break; + case InteractionCommandError.BadArgs: + // implement + break; + case InteractionCommandError.Exception: + // implement + break; + case InteractionCommandError.Unsuccessful: + // implement + break; + default: + break; + } + } + + return Task.CompletedTask; + } + + private Task ContextCommandExecuted(ContextCommandInfo arg1, IInteractionContext arg2, IResult arg3) + { + if (!arg3.IsSuccess) + { + switch (arg3.Error) + { + case InteractionCommandError.UnmetPrecondition: + // implement + break; + case InteractionCommandError.UnknownCommand: + // implement + break; + case InteractionCommandError.BadArgs: + // implement + break; + case InteractionCommandError.Exception: + // implement + break; + case InteractionCommandError.Unsuccessful: + // implement + break; + default: + break; + } + } + + return Task.CompletedTask; + } + + private Task SlashCommandExecuted(SlashCommandInfo arg1, IInteractionContext arg2, IResult arg3) + { + if (!arg3.IsSuccess) + { + switch (arg3.Error) + { + case InteractionCommandError.UnmetPrecondition: + // implement + break; + case InteractionCommandError.UnknownCommand: + // implement + break; + case InteractionCommandError.BadArgs: + // implement + break; + case InteractionCommandError.Exception: + // implement + break; + case InteractionCommandError.Unsuccessful: + // implement + break; + default: + break; + } + } + + return Task.CompletedTask; + } + + private async Task HandleInteraction(SocketInteraction arg) + { + try + { + // create an execution context that matches the generic type parameter of your InteractionModuleBase modules + var ctx = new SocketInteractionContext(_client, arg); + await _commands.ExecuteCommandAsync(ctx, _services); + } + catch (Exception ex) + { + Console.WriteLine(ex); + // if a Slash Command execution fails it is most likely that the original interaction acknowledgement will persist. It is a good idea to delete the original + // response, or at least let the user know that something went wrong during the command execution. + if (arg.Type == InteractionType.ApplicationCommand) + { + await arg.GetOriginalResponseAsync().ContinueWith(async (msg) => await msg.Result.DeleteAsync()); + } + } + } + } +} diff --git a/src/Managing.Infrastructure.Messengers/Discord/DiscordCommands.cs b/src/Managing.Infrastructure.Messengers/Discord/DiscordCommands.cs new file mode 100644 index 0000000..7a7e582 --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Discord/DiscordCommands.cs @@ -0,0 +1,223 @@ +using Discord; +using Discord.Commands; +using Managing.Application.Abstractions; +using Managing.Application.Trading.Commands; +using Managing.Common; +using Managing.Core; +using Managing.Domain.MoneyManagements; +using Managing.Domain.Trades; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Messengers.Discord +{ + public class DiscordCommands : ModuleBase + { + private readonly DiscordSettings _settings; + private readonly IBacktester _backtester; + private readonly string[] _separator = new[] { " " }; + private readonly string _messagePrefix = "Bro, "; + private readonly ICommandHandler _openTradeCommandHandler; + + public DiscordCommands(DiscordSettings settings, + IBacktester backtester, + ICommandHandler openTradeCommandHandler) + { + _settings = settings; + _backtester = backtester; + _openTradeCommandHandler = openTradeCommandHandler; + } + + + [Command("openposition")] + public async Task OpenPosition([Remainder] string rest) + { + Typing(); + + var parameters = rest.Split(_separator, StringSplitOptions.None); + + var exchange = MiscExtensions.ParseEnum(parameters[0]); + var ticker = parameters[1]; + var direction = MiscExtensions.ParseEnum(parameters[2]); + var timeframe = MiscExtensions.ParseEnum(parameters[3]); + var expirationDate = DateTime.Now.AddMinutes(_settings.ButtonExpirationMinutes).ToString("G"); + + var builder = new ComponentBuilder().WithButton("Open Position", + $"{Constants.DiscordButtonAction.OpenPosition}-{exchange}-{ticker}-{direction}-{timeframe}-{expirationDate}"); + await Context.Channel.SendMessageAsync(_messagePrefix + $"you can now click on this button to open a position on {ticker}" + , components: builder.Build()); + } + + [Command("open")] + public async Task Open([Remainder] string rest) + { + await Context.Channel.SendMessageAsync("Let met few seconds to open a position"); + + Typing(); + try + { + var parameters = rest.Split(_separator, StringSplitOptions.None); + var accountName = parameters[0]; + var ticker = MiscExtensions.ParseEnum(parameters[1]); + var direction = MiscExtensions.ParseEnum(parameters[2]); + var riskLevel = MiscExtensions.ParseEnum(parameters[3]); + var timeframe = MiscExtensions.ParseEnum(parameters[4]); + decimal? stopLoss = parameters[5] != null ? Convert.ToDecimal(parameters[5]) : null; + decimal? takeProfit = parameters[6] != null ? Convert.ToDecimal(parameters[6]) : null; + + var moneymanagement = new MoneyManagement + { + BalanceAtRisk = 5, + StopLoss = stopLoss.GetValueOrDefault(), + TakeProfit = takeProfit.GetValueOrDefault(), + + }; + var tradeCommand = new OpenPositionRequest(accountName, moneymanagement, direction, ticker, PositionInitiator.User, DateTime.UtcNow); + var result = await _openTradeCommandHandler.Handle(tradeCommand); + var builder = new ComponentBuilder().WithButton("Close Position", $"{Constants.DiscordButtonAction.ClosePosition}|{result.Open.ExchangeOrderId}"); + await Context.Channel.SendMessageAsync(MessengerHelpers.GetPositionMessage(result), + components: builder.Build()); + } + catch (Exception ex) + { + await Context.Channel.SendMessageAsync($"Something weird happen bro, {ex.Message}"); + } + + } + + [Command("bot")] + public async Task Bot([Remainder] string rest) + { + Typing(); + + if (string.Equals(rest, "enable", StringComparison.OrdinalIgnoreCase)) + { + _settings.BotEnabled = true; + } + if (string.Equals(rest, "disable", StringComparison.OrdinalIgnoreCase)) + { + _settings.BotEnabled = false; + } + await Context.Channel.SendMessageAsync("Bot is " + + (_settings.BotEnabled ? "enabled" : "disabled")); + } + + //[Command("backtest")] + //public async Task Backtest([Remainder] string rest) + //{ + // Typing(); + + // var parameters = rest.Split(_separator, StringSplitOptions.None); + // var ticker = MiscExtensions.ParseEnum(parameters[0]); + // var exchange = MiscExtensions.ParseEnum(parameters[1]); + // var timeframe = MiscExtensions.ParseEnum(parameters[2]); + // var days = parameters[3]; + // var scenario = new Scenario("ScalpingScenario"); + // var strategy = ScenarioHelpers.BuildStrategy(StrategyType.RsiDivergence, timeframe, "RsiDiv", period: 14); + // scenario.AddStrategy(strategy); + // var account = new Account() { Exchange = exchange }; + + // var backtestResult = _backtester.RunScalpingBotBacktest(account, ticker, scenario, timeframe, Convert.ToDouble(days), 1000); + // await Context.Channel.SendMessageAsync(backtestResult.GetStringReport()); + //} + + [Command("run")] + public async Task Run([Remainder] string rest) + { + Typing(); + var parameters = rest.Split(_separator, StringSplitOptions.None); + var botType = MiscExtensions.ParseEnum(parameters[0]); + var accountName = MiscExtensions.ParseEnum(parameters[1]); + var botName = parameters[2]; + var ticker = MiscExtensions.ParseEnum(parameters[3]); + var timeframe = MiscExtensions.ParseEnum(parameters[4]); + + var message = _messagePrefix; + + // TODO : Fix remove mediator + //var result = await _mediator.Send(new StartBotCommand(botType, + // botName, + // ticker, + // "scenario", + // timeframe, + // accountName, + // true)); + + var result = "down"; + message += $"I tried to start the bot called {botName}, his status is now {result}. "; + + if (result == "Up") + { + await Context.Message.AddReactionAsync(new Emoji("👌")); + message += $"this bot gonna watch {ticker} on {timeframe} timeframe."; + } + else + { + await Context.Message.AddReactionAsync(new Emoji("😒")); + message += $"something wrong happen, the bot status is down"; + } + + await Context.Channel.SendMessageAsync(message); + } + + [Command("stop")] + public async Task Stop([Remainder] string rest) + { + Typing(); + var parameters = rest.Split(_separator, StringSplitOptions.None); + var botType = MiscExtensions.ParseEnum(parameters[0]); + var botName = parameters[1]; + var result = "down"; + //var result = await _mediator.Send(new StopBotCommand(botType, botName)); + + await Context.Channel.SendMessageAsync(_messagePrefix + $"the {botType} called {botName} is now {result}"); + } + + [Command("hi")] + public async Task Hi([Remainder] string rest) + { + Typing(); + await Context.Channel.SendMessageAsync("I don't understand yet : " + rest); + } + + [Command("help")] + public async Task Help([Remainder] string rest) + { + Typing(); + + var message = _messagePrefix; + switch (rest) + { + case "backtest": + message += "to run a backtest you should use this pattern : !trader backtest ticker exchange timeframe startFromDays"; + break; + case "run": + message += "to run a bot you should use this pattern : !trader run botType botName ticker timeframe"; + break; + case "stop": + message += "to stop a bot you should use this pattern : !trader stop botType botName"; + break; + case "botType": + message += $"the bot type available are : {BotType.FlippingBot}, {BotType.ScalpingBot}"; + break; + case "timeframe": + message += $"the bot can currently handle only those timeframes : " + + $"{Timeframe.FifteenMinutes}, {Timeframe.ThirtyMinutes}, {Timeframe.OneDay}"; + break; + case "risklevel": + message += $"the bot can currently handle only those riskLevel : " + + $"{RiskLevel.High}"; + break; + default: + message += "I don't no the command"; + break; + } + + await Context.Channel.SendMessageAsync(message); + } + + private async void Typing() + { + await Context.Channel.TriggerTypingAsync(); + } + } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Messengers/Discord/DiscordHelpers.cs b/src/Managing.Infrastructure.Messengers/Discord/DiscordHelpers.cs new file mode 100644 index 0000000..bd2e298 --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Discord/DiscordHelpers.cs @@ -0,0 +1,80 @@ +using Discord; +using Managing.Domain.Statistics; +using Managing.Domain.Trades; + +namespace Managing.Infrastructure.Messengers.Discord; + +public static class DiscordHelpers +{ + public static Embed GetTradersEmbed(List traders, string title) + { + var fields = new List(); + + traders = traders.OrderByDescending(t => t.Winrate).ToList(); + foreach (var trader in traders) + { + fields.Add(new EmbedFieldBuilder + { + Name = $"{GetExplorerUrl(trader.Address)}", + Value = $"Avg Win / Avg Loss / Winrate / ROI \n {trader.AverageWin:#.##}$ / {trader.AverageLoss:#.##}$ / {trader.Winrate}% / {Convert.ToDecimal(trader.Roi) * 100:#.##}%", + }); + } + + var embed = new EmbedBuilder + { + Author = new EmbedAuthorBuilder() { Name = "GMX" }, + Title = $"{title} {DateTime.UtcNow:d}", + Color = Color.Gold, + Fields = fields, + }.Build(); + + return embed; + } + + public static Embed GetEmbed(string address, string title, List fields, Color color) + { + return new EmbedBuilder + { + Author = new EmbedAuthorBuilder() { Name = address }, + Title = title, + Color = color, + Fields = fields, + Url = GetExplorerUrl(address) + }.Build(); + } + + private static string GetExplorerUrl(string key) + { + return $"https://www.tradao.xyz/#/user/{key}/1"; + } + + internal static Embed GetTradesEmbed(List trades, string title) + { + var fields = new List(); + + foreach (var trade in trades) + { + fields.Add(new EmbedFieldBuilder + { + Name = $"{GetExplorerUrl(trade.ExchangeOrderId)}", + Value = $"Side / Ticker / Open / Qty / Leverage / LiqPrice \n {trade.Direction} / {trade.Ticker} / {trade.Price:#.##}$ / {trade.Quantity:#.##}$ / x{trade.Leverage:#.##} / {Convert.ToDecimal(trade.Message):#.##}$", + }); + } + + fields.Add(new EmbedFieldBuilder + { + Name = "Summary", + Value = $"Long / Short / \n {trades.Count(t => t.Direction == Common.Enums.TradeDirection.Long)} / {trades.Count(t => t.Direction == Common.Enums.TradeDirection.Short)}" + }); + + var embed = new EmbedBuilder + { + Author = new EmbedAuthorBuilder() { Name = "GMX" }, + Title = $"{title} {DateTime.UtcNow:d}", + Color = Color.DarkBlue, + Fields = fields, + }.Build(); + + return embed; + } +} diff --git a/src/Managing.Infrastructure.Messengers/Discord/DiscordService.cs b/src/Managing.Infrastructure.Messengers/Discord/DiscordService.cs new file mode 100644 index 0000000..2545dab --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Discord/DiscordService.cs @@ -0,0 +1,557 @@ +using Discord; +using Discord.Commands; +using Discord.Net; +using Discord.WebSocket; +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; +using Managing.Application.Trading; +using Managing.Application.Trading.Commands; +using Managing.Application.Workers.Abstractions; +using Managing.Common; +using Managing.Core; +using Managing.Domain.Statistics; +using Managing.Domain.Trades; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Messengers.Discord +{ + public class DiscordService : IHostedService, IDisposable, IDiscordService + { + private const string _separator = "|"; + private readonly DiscordSocketClient _client; + private readonly CommandService _commandService; + private readonly IServiceProvider _services; + private readonly IExchangeService _exchangeService; + private readonly IMoneyManagementService _moneyManagementService; + private readonly IAccountService _accountService; + private readonly ITradingService _tradingService; + private readonly IStatisticService _statisticService; + + private readonly DiscordSettings _settings; + private readonly ILogger _logger; + + private readonly string _explorerUrl = ""; + + public DiscordService(DiscordSocketClient client, + CommandService commandService, + IServiceProvider services, + DiscordSettings settings, ILogger logger) + { + _client = client; + _commandService = commandService; + _services = services; + _settings = settings; + _logger = logger; + } + + #region Setup + // The hosted service has started + public async Task StartAsync(CancellationToken cancellationToken) + { + if (_settings.HandleUserAction) + { + _client.ButtonExecuted += ButtonHandler; + _commandService.CommandExecuted += CommandExecuted; + _client.SlashCommandExecuted += SlashCommandHandler; + } + + _client.Ready += ClientReady; + _client.Log += Log; + _commandService.Log += Log; + // look for classes implementing ModuleBase to load commands from + await _commandService.AddModulesAsync(GetType().Assembly, _services); + // log in to Discord, using the provided token + await _client.LoginAsync(TokenType.Bot, _settings.Token); + // start bot + await _client.StartAsync(); + } + + private async Task SlashCommandHandler(SocketSlashCommand command) + { + await command.DeferAsync(); + + // Let's add a switch statement for the command name so we can handle multiple commands in one event. + switch (command.Data.Name) + { + case Constants.DiscordSlashCommand.Leaderboard: + await SlashCommands.HandleLeaderboardCommand(_services, command); + break; + case Constants.DiscordSlashCommand.Noobiesboard: + await SlashCommands.HandleNoobiesboardCommand(_services, command); + break; + case Constants.DiscordSlashCommand.LeaderboardPosition: + await SlashCommands.HandleLeadboardPositionCommand(_services, command); + break; + + } + } + + private async Task ClientReady() + { + // set status to online + await _client.SetStatusAsync(UserStatus.Online); + // Discord started as a game chat service, so it has the option to show what games you are playing + // Here the bot will display "Playing dead" while listening + await _client.SetGameAsync(_settings.BotActivity, "https://moon.com", ActivityType.Playing); + + if (!_settings.HandleUserAction) return; + + List applicationCommandProperties = new(); + try + { + var leaderBoardCommand = new SlashCommandBuilder(); + leaderBoardCommand.WithName(Constants.DiscordSlashCommand.Leaderboard); + leaderBoardCommand.WithDescription("Shows the last leaderboard"); + applicationCommandProperties.Add(leaderBoardCommand.Build()); + + var leadboardPositionsCommand = new SlashCommandBuilder(); + leadboardPositionsCommand.WithName(Constants.DiscordSlashCommand.LeaderboardPosition); + leadboardPositionsCommand.WithDescription("Shows the opened position of the leaderboard"); + applicationCommandProperties.Add(leadboardPositionsCommand.Build()); + + var noobiesboardCommand = new SlashCommandBuilder(); + noobiesboardCommand.WithName(Constants.DiscordSlashCommand.Noobiesboard); + noobiesboardCommand.WithDescription("Shows the last Noobies board"); + applicationCommandProperties.Add(noobiesboardCommand.Build()); + + + await _client.BulkOverwriteGlobalApplicationCommandsAsync(applicationCommandProperties.ToArray()); + } + catch (ApplicationCommandException exception) + { + var json = JsonConvert.SerializeObject(exception, Formatting.Indented); + Console.WriteLine(json); + } + } + + public static List GetSlashCommands() + { + List commands = new(); + + + + return commands; + } + + // logging + private async Task Log(LogMessage arg) + { + await Task.Run(() => + { + _logger.LogInformation(arg.ToString()); + }); + } + + private async Task CommandExecuted(Optional command, ICommandContext context, IResult result) + { + // if a command isn't found + if (!command.IsSpecified) + { + await context.Message.AddReactionAsync(new Emoji("🤨")); // eyebrow raised emoji + return; + } + + // log failure to the console + if (!result.IsSuccess) + { + await Log(new LogMessage(LogSeverity.Error, nameof(CommandExecuted), $"Error: {result.ErrorReason}")); + return; + } + // react to message + await context.Message.AddReactionAsync(new Emoji("🤖")); // robot emoji + } + + // the hosted service is stopping + public async Task StopAsync(CancellationToken cancellationToken) + { + await _client.SetGameAsync(null); + await _client.SetStatusAsync(UserStatus.Offline); + await _client.StopAsync(); + _client.Log -= Log; + _client.Ready -= ClientReady; + _commandService.Log -= Log; + _commandService.CommandExecuted -= CommandExecuted; + _client.ButtonExecuted -= ButtonHandler; + _client.SlashCommandExecuted -= SlashCommandHandler; + } + public void Dispose() + { + _client?.Dispose(); + } + #endregion + + #region In + public async Task ButtonHandler(SocketMessageComponent component) + { + var parameters = component.Data.CustomId.Split(new[] { _separator }, StringSplitOptions.None); + var command = parameters[0]; + + if (component.User.GlobalName != "crypto_saitama") + { + await component.Channel.SendMessageAsync("Sorry bro, this feature is not accessible for you.. Do not hesitate to send me approx. 456 121 $ and i give you full access"); + } + else + { + switch (command) + { + case Constants.DiscordButtonAction.OpenPosition: + await OpenPosition(component, parameters); + break; + case Constants.DiscordButtonAction.ClosePosition: + await ClosePosition(component, parameters); + break; + case Constants.DiscordButtonAction.CopyPosition: + await CopyPosition(component, parameters); + break; + default: + break; + } + } + } + + private async Task CopyPosition(SocketMessageComponent component, string[] parameters) + { + await component.Channel.SendMessageAsync("Let met few seconds to copy this position"); + await component.Channel.TriggerTypingAsync(); + + var json = MiscExtensions.Base64Decode(parameters[1]); + var trade = JsonConvert.DeserializeObject(json); + await OpenPosition(component, trade.AccountName, trade.MoneyManagementName, PositionInitiator.CopyTrading, trade.Ticker, trade.Direction, Timeframe.FifteenMinutes, DateTime.Now.AddMinutes(trade.ExpirationMinute), true, trade.Leverage); ; + } + + public async Task OpenPosition(SocketMessageComponent component, string[] parameters) + { + await component.Channel.SendMessageAsync("Let met few seconds to open a position"); + await component.Channel.TriggerTypingAsync(); + + var accountName = MiscExtensions.ParseEnum(parameters[1]); + var ticker = MiscExtensions.ParseEnum(parameters[2]); + var direction = MiscExtensions.ParseEnum(parameters[3]); + var timeframe = MiscExtensions.ParseEnum(parameters[4]); + var moneyManagementName = parameters[5]; + var expiration = DateTime.Parse(parameters[6]); + + await OpenPosition(component, accountName, moneyManagementName, PositionInitiator.User, ticker, direction, timeframe, expiration, false); + } + + private async Task OpenPosition(SocketMessageComponent component, string accountName, string moneyManagement, PositionInitiator initiator, Ticker ticker, TradeDirection direction, Timeframe timeframe, DateTime expiration, bool ignoreSLTP, decimal? leverage = null) + { + if (DateTime.Now > expiration) + { + await component.Channel.SendMessageAsync("Sorry I can't open position because you tried to click on a expired button."); + } + else + { + var exchangeService = (IExchangeService)_services.GetService(typeof(IExchangeService)); + var moneyManagementService = (IMoneyManagementService)_services.GetService(typeof(IMoneyManagementService)); + var accountService = (IAccountService)_services.GetService(typeof(IAccountService)); + var tradingService = (ITradingService)_services.GetService(typeof(ITradingService)); + + var tradeCommand = new OpenPositionRequest( + accountName, + await moneyManagementService.GetMoneyMangement(moneyManagement), + direction, + ticker, + initiator, + DateTime.UtcNow, + ignoreSLTP: ignoreSLTP); + var position = await new OpenPositionCommandHandler(exchangeService, accountService, tradingService) + .Handle(tradeCommand); + + var builder = new ComponentBuilder().WithButton("Close Position", $"{Constants.DiscordButtonAction.ClosePosition}{_separator}{position.Identifier}"); + + await component.Channel.SendMessageAsync(MessengerHelpers.GetPositionMessage(position), + components: builder.Build()); + } + } + + + private string GetClosingPositionMessage(Position position) + { + return $"Closing : {position.OriginDirection} {position.Open.Ticker} \n" + + $"Open Price : {position.Open.Price} \n" + + $"Closing Price : {position.Open.Price} \n" + + $"Quantity :{position.Open.Quantity} \n" + + $"PNL : {position.ProfitAndLoss.Net} $"; + } + + private async Task ClosePosition(SocketMessageComponent component, string[] parameters) + { + var exchangeService = (IExchangeService)_services.GetService(typeof(IExchangeService)); + var accountService = (IAccountService)_services.GetService(typeof(IAccountService)); + var tradingService = (ITradingService)_services.GetService(typeof(ITradingService)); + + await component.RespondAsync("Alright, let met few seconds to close this position"); + var position = _tradingService.GetPositionByIdentifier(parameters[1]); + var command = new ClosePositionCommand(position); + var result = await new ClosePositionCommandHandler(exchangeService, accountService, tradingService).Handle(command); + var fields = new List() + { + new EmbedFieldBuilder + { + Name = "Direction", + Value = position.OriginDirection, + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Open Price", + Value = $"{position.Open.Price:#.##}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Quantity", + Value = $"{position.Open.Quantity:#.##}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Pnl", + Value = $"{position.ProfitAndLoss.Net:#.##}", + IsInline = true + }, + }; + var embed = DiscordHelpers.GetEmbed(position.AccountName, $"Position status is now {result.Status}", fields, position.ProfitAndLoss.Net > 0 ? Color.Green : Color.Red); + await component.Channel.SendMessageAsync("", embed: embed); + } + + #endregion + + + #region Out + public async Task SendSignal(string message) + { + var channel = _client.GetChannel(_settings.SignalChannelId) as IMessageChannel; + var builder = new ComponentBuilder().WithButton("Open Position", $"openposition{_separator}"); + await channel.SendMessageAsync(message, components: builder.Build()); + } + + public async Task SendSignal(string message, TradingExchanges exchange, Ticker ticker, TradeDirection direction, Timeframe timeframe) + { + var expirationDate = DateTime.Now.AddMinutes(_settings.ButtonExpirationMinutes).ToString("G"); + var channel = _client.GetChannel(_settings.SignalChannelId) as IMessageChannel; + var builder = new ComponentBuilder().WithButton("Open Position", $"{Constants.DiscordButtonAction.OpenPosition}{_separator}{exchange}{_separator}{ticker}{_separator}{direction}{_separator}{timeframe}{_separator}{expirationDate}"); + await channel.SendMessageAsync(message, components: builder.Build()); + } + + public async Task SendIncreasePosition(string address, Trade trade, string copyAccountName, Trade? oldTrade = null) + { + var channel = _client.GetChannel(_settings.CopyTradingChannelId) as IMessageChannel; + + + var fields = new List() + { + new EmbedFieldBuilder + { + Name = "Last size update", + Value = $"{trade.Date:s}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Entry Price", + Value = $"{trade.Price:#.##} $", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Quantity", + Value = $"{trade.Quantity / trade.Leverage:#.##}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Leverage", + Value = $"x{trade.Leverage:#.##}", + IsInline = true + } + }; + + if (oldTrade != null) + { + fields.Add(new EmbedFieldBuilder { Name = "Increasy by", Value = $"{(trade.Quantity - oldTrade.Quantity) / trade.Leverage:#.##} $" }); + } + + var titlePrefix = oldTrade != null ? "Increase " : ""; + + var builder = new ComponentBuilder(); + + var moneyManagementService = (IMoneyManagementService)_services.GetService(typeof(IMoneyManagementService)); + var moneyManagements = moneyManagementService.GetMoneyMangements(); + + foreach (var mm in moneyManagements) + { + var data = new CopyTradeData + { + Direction = trade.Direction, + Ticker = trade.Ticker, + AccountName = copyAccountName, + ExpirationMinute = 10, + Leverage = trade.Leverage, + }; + data.MoneyManagementName = mm.Name; + var encodedData = MiscExtensions.Base64Encode(JsonConvert.SerializeObject(data)); + + if (oldTrade == null) + { + builder.WithButton($"Copy with {mm.Name}", $"{Constants.DiscordButtonAction.CopyPosition}{_separator}{encodedData}"); + } + else + { + builder.WithButton($"Increase with {mm.Name}", $"{Constants.DiscordButtonAction.CopyPosition}{_separator}{encodedData}"); + } + } + + + var embed = DiscordHelpers.GetEmbed(address, $"{titlePrefix}{trade.Direction} {trade.Ticker}", fields, trade.Direction == TradeDirection.Long ? Color.Green : Color.Red); + await channel.SendMessageAsync("", components: builder.Build(), embed: embed); + } + + public async Task SendPosition(string message, TradingExchanges exchange, Ticker ticker, TradeDirection direction, Timeframe timeframe) + { + var expirationDate = DateTime.Now.AddMinutes(_settings.ButtonExpirationMinutes).ToString("G"); + var channel = _client.GetChannel(_settings.SignalChannelId) as IMessageChannel; + var builder = new ComponentBuilder().WithButton("Open Position", $"{Constants.DiscordButtonAction.OpenPosition}{_separator}{exchange}{_separator}{ticker}{_separator}{direction}{_separator}{timeframe}{_separator}{expirationDate}"); + await channel.SendMessageAsync(message, components: builder.Build()); + } + + public async Task SendMessage(string message) + { + var channel = _client.GetChannel(_settings.TradesChannelId) as IMessageChannel; + await channel.SendMessageAsync(message); + } + + public async Task SendClosingPosition(Position position) + { + var channel = _client.GetChannel(_settings.TradesChannelId) as IMessageChannel; + await channel.SendMessageAsync(GetClosingPositionMessage(position)); + } + + public async Task SendTradeMessage(string message, bool isBadBehavior = false) + { + var channel = _client.GetChannel(isBadBehavior ? _settings.TroublesChannelId : _settings.TradesChannelId) as IMessageChannel; + await channel.SendMessageAsync(message); + } + + public async Task SendClosedPosition(string address, Trade oldTrade) + { + var fields = new List() + { + new EmbedFieldBuilder + { + Name = "Last size update", + Value = $"{oldTrade.Date:s}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Entry Price", + Value = $"{oldTrade.Price:#.##} $", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Quantity", + Value = $"{oldTrade.Quantity / oldTrade.Leverage:#.##}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Leverage", + Value = $"x{oldTrade.Leverage:#.##}", + IsInline = true + } + }; + + var embed = DiscordHelpers.GetEmbed(address, $"Closed {oldTrade.Direction} {oldTrade.Ticker}", fields, oldTrade.Direction == TradeDirection.Long ? Color.DarkGreen : Color.DarkRed); + var channel = _client.GetChannel(_settings.CopyTradingChannelId) as IMessageChannel; + await channel.SendMessageAsync("", embed: embed); + } + + + public async Task SendDecreasePosition(string address, Trade trade, decimal decreaseAmount) + { + var fields = new List() + { + new EmbedFieldBuilder + { + Name = "Last size update", + Value = $"{trade.Date:s}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Entry Price", + Value = $"{trade.Price:#.##} $", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Quantity", + Value = $"{trade.Quantity / trade.Leverage:#.##}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Leverage", + Value = $"x{trade.Leverage:#.##}", + IsInline = true + }, + new EmbedFieldBuilder + { + Name = "Decrease amount", + Value = $"{decreaseAmount:#.##} $" + } + }; + + var embed = DiscordHelpers.GetEmbed(address, $"Decrease {trade.Direction} {trade.Ticker}", fields, Color.Blue); + var channel = _client.GetChannel(_settings.CopyTradingChannelId) as IMessageChannel; + await channel.SendMessageAsync("", embed: embed); + } + + + public async Task SendPosition(Position position) + { + var channel = _client.GetChannel(_settings.TradesChannelId) as IMessageChannel; + var builder = new ComponentBuilder().WithButton("Close Position", $"{Constants.DiscordButtonAction.ClosePosition}{_separator}{position.Open.ExchangeOrderId}"); + await channel.SendMessageAsync(MessengerHelpers.GetPositionMessage(position), components: builder.Build()); + } + + public async Task SendBestTraders(List traders) + { + var channel = _client.GetChannel(_settings.LeaderboardChannelId) as IMessageChannel; + await channel.SendMessageAsync("", embed: DiscordHelpers.GetTradersEmbed(traders, "Leaderboard")); + + } + + public async Task SendBadTraders(List traders) + { + var channel = _client.GetChannel(_settings.NoobiesboardChannelId) as IMessageChannel; + await channel.SendMessageAsync("", embed: DiscordHelpers.GetTradersEmbed(traders, "Noobiesboard")); + } + + #endregion + + public class CopyTradeData + { + [JsonProperty(PropertyName = "D")] + public TradeDirection Direction { get; set; } + [JsonProperty(PropertyName = "T")] + public Ticker Ticker { get; set; } + [JsonProperty(PropertyName = "A")] + public string AccountName { get; set; } + [JsonProperty(PropertyName = "E")] + public int ExpirationMinute { get; set; } + [JsonProperty(PropertyName = "L")] + public decimal Leverage { get; set; } + [JsonProperty(PropertyName = "M")] + public string MoneyManagementName { get; internal set; } + } + + + } +} diff --git a/src/Managing.Infrastructure.Messengers/Discord/DiscordSettings.cs b/src/Managing.Infrastructure.Messengers/Discord/DiscordSettings.cs new file mode 100644 index 0000000..3315998 --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Discord/DiscordSettings.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Configuration; + +namespace Managing.Infrastructure.Messengers.Discord +{ + public class DiscordSettings + { + public DiscordSettings(IConfiguration config) + { + Token = config.GetValue("Discord:TokenId"); + SignalChannelId = config.GetValue("Discord:SignalChannelId"); + CopyTradingChannelId = config.GetValue("Discord:CopyTradingChannelId"); + TradesChannelId = config.GetValue("Discord:TradesChannelId"); + TroublesChannelId = config.GetValue("Discord:TroublesChannelId"); + RequestsChannelId = config.GetValue("Discord:RequestsChannelId"); + LeaderboardChannelId = config.GetValue("Discord:LeaderboardChannelId"); + NoobiesboardChannelId = config.GetValue("Discord:NoobiesboardChannelId"); + ButtonExpirationMinutes = config.GetValue("Discord:ButtonExpirationMinutes"); + HandleUserAction = config.GetValue("Discord:HandleUserAction"); + BotActivity = config.GetValue("Discord:BotActivity"); + BotEnabled = true; + } + + public int ButtonExpirationMinutes { get; set; } + public bool HandleUserAction { get; } + public string BotActivity { get; } + public string Token { get; } + public ulong SignalChannelId { get; } + public ulong CopyTradingChannelId { get; } + public ulong TradesChannelId { get; } + public ulong TroublesChannelId { get; } + public ulong RequestsChannelId { get; } + public bool BotEnabled { get; set; } + public ulong LeaderboardChannelId { get; set; } + public ulong NoobiesboardChannelId { get; set; } + + } +} diff --git a/src/Managing.Infrastructure.Messengers/Discord/MessengerHelpers.cs b/src/Managing.Infrastructure.Messengers/Discord/MessengerHelpers.cs new file mode 100644 index 0000000..dbc78de --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Discord/MessengerHelpers.cs @@ -0,0 +1,15 @@ +using Managing.Domain.Trades; + +namespace Managing.Infrastructure.Messengers.Discord; + +public class MessengerHelpers +{ + public static string GetPositionMessage(Position position) + { + return $"Position : {position.OriginDirection} {position.Open.Ticker} \n" + + $"Open Price : {position.Open.Price} \n" + + $"Quantity : {position.Open.Quantity}. \n" + + $"SL : {position.StopLoss.Price} \n" + + $"TP : {position.TakeProfit1.Price}"; + } +} diff --git a/src/Managing.Infrastructure.Messengers/Discord/SlashCommands.cs b/src/Managing.Infrastructure.Messengers/Discord/SlashCommands.cs new file mode 100644 index 0000000..aab398e --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Discord/SlashCommands.cs @@ -0,0 +1,28 @@ +using Discord.WebSocket; +using Managing.Application.Workers.Abstractions; + +namespace Managing.Infrastructure.Messengers.Discord; + +public static class SlashCommands +{ + public static async Task HandleLeaderboardCommand(IServiceProvider service, SocketSlashCommand command) + { + var statisticService = (IStatisticService)service.GetService(typeof(IStatisticService)); + var traders = statisticService.GetBestTraders(); + await command.FollowupAsync(embed: DiscordHelpers.GetTradersEmbed(traders, "Leaderboard"), ephemeral: true); + } + + public static async Task HandleNoobiesboardCommand(IServiceProvider service, SocketSlashCommand command) + { + var statisticService = (IStatisticService)service.GetService(typeof(IStatisticService)); + var traders = statisticService.GetBadTraders(); + await command.FollowupAsync(embed: DiscordHelpers.GetTradersEmbed(traders, "Noobiesboard"), ephemeral: true); + } + + public static async Task HandleLeadboardPositionCommand(IServiceProvider service, SocketSlashCommand command) + { + var statisticService = (IStatisticService)service.GetService(typeof(IStatisticService)); + var trades = await statisticService.GetLeadboardPositons(); + await command.FollowupAsync(embed: DiscordHelpers.GetTradesEmbed(trades, "Leaderboard Open position"), ephemeral: true); + } +} diff --git a/src/Managing.Infrastructure.Messengers/Managing.Infrastructure.Messengers.csproj b/src/Managing.Infrastructure.Messengers/Managing.Infrastructure.Messengers.csproj new file mode 100644 index 0000000..2b99e86 --- /dev/null +++ b/src/Managing.Infrastructure.Messengers/Managing.Infrastructure.Messengers.csproj @@ -0,0 +1,19 @@ + + + + net7.0 + enable + + + + + + + + + + + + + + diff --git a/src/Managing.Infrastructure.MongoDb/Attributes/BsonCollectionAttribute.cs b/src/Managing.Infrastructure.MongoDb/Attributes/BsonCollectionAttribute.cs new file mode 100644 index 0000000..3a5b35d --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Attributes/BsonCollectionAttribute.cs @@ -0,0 +1,13 @@ +namespace Managing.Infrastructure.MongoDb.Attributes +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public class BsonCollectionAttribute : Attribute + { + public string CollectionName { get; } + + public BsonCollectionAttribute(string collectionName) + { + CollectionName = collectionName; + } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/AccountDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/AccountDto.cs new file mode 100644 index 0000000..6d82cde --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/AccountDto.cs @@ -0,0 +1,14 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections; + +[BsonCollection("Accounts")] +public class AccountDto : Document +{ + public string Name { get; set; } + public Exchanges Exchanges { get; set; } + public string Key { get; set; } + public string Secret { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.MongoDb/Collections/BacktestDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/BacktestDto.cs new file mode 100644 index 0000000..e0e6c81 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/BacktestDto.cs @@ -0,0 +1,24 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("Backtests")] + public class BacktestDto : Document + { + public decimal FinalPnl { get; set; } + public int WinRate { get; set; } + public decimal GrowthPercentage { get; set; } + public decimal HodlPercentage { get; set; } + public string Ticker { get; set; } + public string Scenario { get; set; } + public List Positions { get; set; } + public List Signals { get; set; } + public Timeframe Timeframe { get; set; } + public RiskLevel RiskLevel { get; set; } + public string AccountName { get; set; } + public List Candles { get; set; } + public BotType BotType { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/CandleDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/CandleDto.cs new file mode 100644 index 0000000..a0d8f03 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/CandleDto.cs @@ -0,0 +1,28 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using MongoDB.Bson.Serialization.Attributes; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("Candles")] + public class CandleDto : Document + { + public Exchanges Exchange { get; set; } + public Timeframe Timeframe { get; set; } + public string Ticker { get; set; } + [BsonDateTimeOptions] + public DateTime OpenTime { get; set; } + [BsonDateTimeOptions] + public DateTime CloseTime { get; set; } + public decimal Open { get; set; } + public decimal Close { get; set; } + public decimal High { get; set; } + public decimal Low { get; set; } + public decimal BaseVolume { get; set; } + public decimal QuoteVolume { get; set; } + public int TradeCount { get; set; } + public decimal TakerBuyBaseVolume { get; set; } + public decimal TakerBuyQuoteVolume { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/MoneyManagementDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/MoneyManagementDto.cs new file mode 100644 index 0000000..11bf506 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/MoneyManagementDto.cs @@ -0,0 +1,18 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("MoneyManagement")] + public class MoneyManagementDto : Document + { + public Timeframe Timeframe { get; set; } + public RiskLevel RiskLevel { get; set; } + public decimal BalanceAtRisk { get; set; } + public decimal StopLoss { get; set; } + public decimal TakeProfit { get; set; } + public decimal QuantityTakeProfit { get; set; } + public decimal Leverage { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/PositionDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/PositionDto.cs new file mode 100644 index 0000000..e6837f8 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/PositionDto.cs @@ -0,0 +1,24 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using MongoDB.Bson.Serialization.Attributes; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("Positions")] + public class PositionDto : Document + { + [BsonDateTimeOptions] + public DateTime Date { get; set; } + public TradeDto Open { get; set; } + public TradeDto StopLoss { get; set; } + public TradeDto TakeProfit1 { get; set; } + public TradeDto TakeProfit2 { get; set; } + public decimal ProfitAndLoss { get; set; } + public TradeDirection OriginDirection { get; set; } + public string Identifier { get; set; } + public TradeStatus Status { get; set; } + public string SignalIdentifier { get; set; } + public string AccountName { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/ScenarioDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/ScenarioDto.cs new file mode 100644 index 0000000..fa51ba7 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/ScenarioDto.cs @@ -0,0 +1,12 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("Scenarios")] + public class ScenarioDto : Document + { + public string Name { get; set; } + public List Strategies { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/SignalDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/SignalDto.cs new file mode 100644 index 0000000..ba20b5f --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/SignalDto.cs @@ -0,0 +1,20 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("Signals")] + public class SignalDto : Document + { + public TradeDirection Direction { get; set; } + public Confidence Confidence { get; set; } + public DateTime Date { get; set; } + public CandleDto Candle { get; set; } + public string Identifier { get; set; } + public string Ticker { get; set; } + public SignalStatus Status { get; set; } + public Timeframe Timeframe { get; set; } + public StrategyType Type { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/StrategyDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/StrategyDto.cs new file mode 100644 index 0000000..f438eb0 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/StrategyDto.cs @@ -0,0 +1,21 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("Strategies")] + public class StrategyDto : Document + { + public StrategyType Type { get; set; } + public Timeframe Timeframe { get; set; } + public string Name { get; set; } + public int? Period { get; set; } + public int? FastPeriods { get; set; } + public int? SlowPeriods { get; set; } + public int? SignalPeriods { get; set; } + public double? Multiplier { get; set; } + public int? StochPeriods { get; set; } + public int? SmoothPeriods { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/TopVolumeTickerDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/TopVolumeTickerDto.cs new file mode 100644 index 0000000..20813f0 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/TopVolumeTickerDto.cs @@ -0,0 +1,15 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections; + +[BsonCollection("TopVolumeTickers")] +public class TopVolumeTickerDto : Document +{ + public Ticker Ticker { get; set; } + public DateTime Date { get; set; } + public decimal Volume { get; set; } + public int Rank { get; set; } + public Exchanges Exchange { get; set; } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/TradeDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/TradeDto.cs new file mode 100644 index 0000000..8e6afa0 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/TradeDto.cs @@ -0,0 +1,24 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using MongoDB.Bson.Serialization.Attributes; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections +{ + [BsonCollection("Trades")] + public class TradeDto : Document + { + [BsonDateTimeOptions] + public DateTime Date { get; set; } + public TradeDirection Direction { get; set; } + public TradeStatus Status { get; set; } + public TradeType TradeType { get; set; } + public string Ticker { get; set; } + public decimal Fee { get; set; } + public decimal Quantity { get; set; } + public decimal Price { get; set; } + public decimal Leverage { get; set; } + public string ExchangeOrderId { get; set; } + public string Message { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Collections/WorkerDto.cs b/src/Managing.Infrastructure.MongoDb/Collections/WorkerDto.cs new file mode 100644 index 0000000..7abcc36 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Collections/WorkerDto.cs @@ -0,0 +1,15 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.MongoDb.Collections; + +[BsonCollection("Workers")] +public class WorkerDto : Document +{ + public WorkerType WorkerType { get; set; } + public DateTime StartTime { get; set; } + public DateTime? LastRunTime { get; set; } + public int ExecutionCount { get; set; } + public TimeSpan Delay { get; set; } +} diff --git a/src/Managing.Infrastructure.MongoDb/Configurations/Document.cs b/src/Managing.Infrastructure.MongoDb/Configurations/Document.cs new file mode 100644 index 0000000..c0e04dd --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Configurations/Document.cs @@ -0,0 +1,13 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Managing.Infrastructure.MongoDb.Configurations +{ + public abstract class Document : IDocument + { + [BsonId] + public ObjectId Id { get; set; } + [BsonDateTimeOptions] + public DateTime CreatedAt => Id.CreationTime; + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Configurations/IDocument.cs b/src/Managing.Infrastructure.MongoDb/Configurations/IDocument.cs new file mode 100644 index 0000000..85e74b3 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Configurations/IDocument.cs @@ -0,0 +1,14 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Managing.Infrastructure.MongoDb.Configurations +{ + public interface IDocument + { + [BsonId] + [BsonRepresentation(BsonType.String)] + ObjectId Id { get; set; } + + DateTime CreatedAt { get; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Configurations/IManagingDatabaseSettings.cs b/src/Managing.Infrastructure.MongoDb/Configurations/IManagingDatabaseSettings.cs new file mode 100644 index 0000000..9d57568 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Configurations/IManagingDatabaseSettings.cs @@ -0,0 +1,8 @@ +namespace Managing.Infrastructure.MongoDb +{ + public interface IManagingDatabaseSettings + { + string ConnectionString { get; set; } + string DatabaseName { get; set; } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Configurations/ManagingDatabaseSettings.cs b/src/Managing.Infrastructure.MongoDb/Configurations/ManagingDatabaseSettings.cs new file mode 100644 index 0000000..9235ffb --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Configurations/ManagingDatabaseSettings.cs @@ -0,0 +1,7 @@ +namespace Managing.Infrastructure.MongoDb; + +public class ManagingDatabaseSettings : IManagingDatabaseSettings +{ + public string ConnectionString { get; set; } + public string DatabaseName { get; set; } +} diff --git a/src/Managing.Infrastructure.MongoDb/IMongoRepository.cs b/src/Managing.Infrastructure.MongoDb/IMongoRepository.cs new file mode 100644 index 0000000..93a8836 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/IMongoRepository.cs @@ -0,0 +1,55 @@ +using Managing.Infrastructure.MongoDb.Configurations; +using System.Linq.Expressions; + +namespace Managing.Infrastructure.MongoDb +{ + public interface IMongoRepository where TDocument : IDocument + { + IQueryable AsQueryable(); + + IEnumerable FilterBy( + Expression> filterExpression); + + IEnumerable FindAll(); + + IEnumerable FilterBy( + Expression> filterExpression, + Expression> projectionExpression); + + TDocument FindOne(Expression> filterExpression); + + Task FindOneAsync(Expression> filterExpression); + + TDocument FindById(string id); + + Task FindByIdAsync(string id); + + void InsertOne(TDocument document); + + Task InsertOneAsync(TDocument document); + + void InsertMany(ICollection documents); + + Task InsertManyAsync(ICollection documents); + + void ReplaceOne(TDocument document); + + Task ReplaceOneAsync(TDocument document); + + void DeleteOne(Expression> filterExpression); + + Task DeleteOneAsync(Expression> filterExpression); + + void DeleteById(string id); + + Task DeleteByIdAsync(string id); + + void DeleteMany(Expression> filterExpression); + + Task DeleteManyAsync(Expression> filterExpression); + + void Update(TDocument entity); + void CreateIndex(string column); + void DropCollection(); + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj b/src/Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj new file mode 100644 index 0000000..821412a --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + AnyCPU;x64 + + + + + + + + + + + + diff --git a/src/Managing.Infrastructure.MongoDb/MongoHelpers.cs b/src/Managing.Infrastructure.MongoDb/MongoHelpers.cs new file mode 100644 index 0000000..0dbf2c0 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/MongoHelpers.cs @@ -0,0 +1,20 @@ +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Managing.Infrastructure.MongoDb +{ + public static class MongoHelpers + { + public static async Task EnsureIndexExists(this IMongoDatabase database, string collectionName, string indexName) + { + var collection = database.GetCollection(collectionName); + var index = new BsonDocument + { + {indexName, 1} + }; + + var indexModel = new CreateIndexModel(index, new CreateIndexOptions { Unique = true }); + await collection.Indexes.CreateOneAsync(indexModel).ConfigureAwait(false); + } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/MongoRepository.cs b/src/Managing.Infrastructure.MongoDb/MongoRepository.cs new file mode 100644 index 0000000..9ac0086 --- /dev/null +++ b/src/Managing.Infrastructure.MongoDb/MongoRepository.cs @@ -0,0 +1,173 @@ +using Managing.Infrastructure.MongoDb.Attributes; +using Managing.Infrastructure.MongoDb.Configurations; +using MongoDB.Bson; +using MongoDB.Driver; +using System.Linq.Expressions; + +namespace Managing.Infrastructure.MongoDb +{ + public class MongoRepository : IMongoRepository + where TDocument : IDocument + { + private readonly IMongoCollection _collection; + private readonly IMongoDatabase _database; + + public MongoRepository(IManagingDatabaseSettings settings) + { + _database = new MongoClient(settings.ConnectionString).GetDatabase(settings.DatabaseName); + _collection = _database.GetCollection(GetCollectionName(typeof(TDocument))); + } + + private protected string GetCollectionName(Type documentType) + { + return ((BsonCollectionAttribute)documentType.GetCustomAttributes( + typeof(BsonCollectionAttribute), + true) + .FirstOrDefault())?.CollectionName; + } + + public virtual IQueryable AsQueryable() + { + return _collection.AsQueryable(); + } + + public virtual IEnumerable FilterBy( + Expression> filterExpression) + { + return _collection.Find(filterExpression).ToEnumerable(); + } + + public virtual IEnumerable FindAll() + { + return _collection.Find(_ => true).ToEnumerable(); + } + + public virtual IEnumerable FilterBy( + Expression> filterExpression, + Expression> projectionExpression) + { + return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable(); + } + + public virtual TDocument FindOne(Expression> filterExpression) + { + return _collection.Find(filterExpression).FirstOrDefault(); + } + + public virtual Task FindOneAsync(Expression> filterExpression) + { + return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync()); + } + + public virtual TDocument FindById(string id) + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + return _collection.Find(filter).SingleOrDefault(); + } + + public virtual Task FindByIdAsync(string id) + { + return Task.Run(() => + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + return _collection.Find(filter).SingleOrDefaultAsync(); + }); + } + + + public virtual void InsertOne(TDocument document) + { + _collection.InsertOne(document); + } + + public virtual Task InsertOneAsync(TDocument document) + { + return Task.Run(() => _collection.InsertOneAsync(document)); + } + + public void InsertMany(ICollection documents) + { + _collection.InsertMany(documents); + } + + public void DropCollection() + { + _database.DropCollection(GetCollectionName(typeof(TDocument))); + } + + public virtual async Task InsertManyAsync(ICollection documents) + { + await _collection.InsertManyAsync(documents); + } + + public void ReplaceOne(TDocument document) + { + var filter = Builders.Filter.Eq(doc => doc.Id, document.Id); + _collection.FindOneAndReplace(filter, document); + } + + public virtual async Task ReplaceOneAsync(TDocument document) + { + var filter = Builders.Filter.Eq(doc => doc.Id, document.Id); + await _collection.FindOneAndReplaceAsync(filter, document); + } + + public void Update(TDocument entity) + { + if (entity.Id == ObjectId.Empty) + { + entity.Id = ObjectId.GenerateNewId(); + } + + var option = new ReplaceOptions { IsUpsert = true }; + _collection.ReplaceOne(x => entity != null && x.Id == entity.Id, entity, option); + } + + public void DeleteOne(Expression> filterExpression) + { + _collection.FindOneAndDelete(filterExpression); + } + + public Task DeleteOneAsync(Expression> filterExpression) + { + return Task.Run(() => _collection.FindOneAndDeleteAsync(filterExpression)); + } + + public void DeleteById(string id) + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + _collection.FindOneAndDelete(filter); + } + + public Task DeleteByIdAsync(string id) + { + return Task.Run(() => + { + var objectId = new ObjectId(id); + var filter = Builders.Filter.Eq(doc => doc.Id, objectId); + _collection.FindOneAndDeleteAsync(filter); + }); + } + + public void DeleteMany(Expression> filterExpression) + { + _collection.DeleteMany(filterExpression); + } + + public Task DeleteManyAsync(Expression> filterExpression) + { + return _collection.DeleteManyAsync(filterExpression); + } + + public virtual void CreateIndex(string column) + { + var keys = Builders.IndexKeys.Ascending(column); + var indexOptions = new CreateIndexOptions { Unique = true }; + var model = new CreateIndexModel(keys, indexOptions); + _collection.Indexes.CreateOne(model); + } + } +} diff --git a/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/MoneyManagement.bson.gz b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/MoneyManagement.bson.gz new file mode 100644 index 0000000..cb79d13 Binary files /dev/null and b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/MoneyManagement.bson.gz differ diff --git a/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/MoneyManagement.metadata.json.gz b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/MoneyManagement.metadata.json.gz new file mode 100644 index 0000000..cf75afd Binary files /dev/null and b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/MoneyManagement.metadata.json.gz differ diff --git a/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Scenarios.bson.gz b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Scenarios.bson.gz new file mode 100644 index 0000000..eb949a5 Binary files /dev/null and b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Scenarios.bson.gz differ diff --git a/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Scenarios.metadata.json.gz b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Scenarios.metadata.json.gz new file mode 100644 index 0000000..c98ef8b Binary files /dev/null and b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Scenarios.metadata.json.gz differ diff --git a/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Strategies.bson.gz b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Strategies.bson.gz new file mode 100644 index 0000000..03393fe Binary files /dev/null and b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Strategies.bson.gz differ diff --git a/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Strategies.metadata.json.gz b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Strategies.metadata.json.gz new file mode 100644 index 0000000..743fc31 Binary files /dev/null and b/src/Managing.Infrastructure.MongoDb/Seeds/ManagingDb/Strategies.metadata.json.gz differ diff --git a/src/Managing.Infrastructure.Storage/CacheService.cs b/src/Managing.Infrastructure.Storage/CacheService.cs new file mode 100644 index 0000000..08f07ba --- /dev/null +++ b/src/Managing.Infrastructure.Storage/CacheService.cs @@ -0,0 +1,74 @@ +using Microsoft.Extensions.Caching.Distributed; +using Managing.Application.Abstractions; +using Newtonsoft.Json; + +namespace Managing.Infrastructure.Storage +{ + public class CacheService : ICacheService + { + private readonly IDistributedCache distributedCache; + + public CacheService(IDistributedCache distributedCache) + { + this.distributedCache = distributedCache; + } + + public string GetValue(string key) + { + return distributedCache.GetString(key); + } + + public T GetValue(string key) + { + var cachedData = distributedCache.GetString(key); + + if (!string.IsNullOrEmpty(cachedData)) + { + return JsonConvert.DeserializeObject(cachedData); + } + + return default(T); + } + + public void RemoveValue(string key) + { + distributedCache.Remove(key); + } + + public string SaveValue(string name, string value) + { + distributedCache.SetString(name, value); + return name; + } + + public void SaveValue(string name, T value, TimeSpan slidingExpiration) + { + var options = new DistributedCacheEntryOptions() + { + SlidingExpiration = slidingExpiration + }; + + distributedCache.SetString(name, JsonConvert.SerializeObject(value), options); + } + + public T GetOrSave(string name, Func action, TimeSpan slidingExpiration) + { + var cachedData = distributedCache.GetString(name); + + if (!string.IsNullOrEmpty(cachedData)) + { + return JsonConvert.DeserializeObject(cachedData); + } + + var options = new DistributedCacheEntryOptions() + { + SlidingExpiration = slidingExpiration + }; + + var result = action(); + + distributedCache.SetString(name, JsonConvert.SerializeObject(result), options); + return result; + } + } +} diff --git a/src/Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj b/src/Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj new file mode 100644 index 0000000..6d28033 --- /dev/null +++ b/src/Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj @@ -0,0 +1,19 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + + + + + + + diff --git a/src/Managing.Infrastructure.Storage/TaskCache.cs b/src/Managing.Infrastructure.Storage/TaskCache.cs new file mode 100644 index 0000000..b89e52e --- /dev/null +++ b/src/Managing.Infrastructure.Storage/TaskCache.cs @@ -0,0 +1,82 @@ +using Managing.Application.Abstractions; +using Managing.Core; +//using Microsoft.Extensions.Caching.Memory; +using System.Runtime.Caching; + +namespace Managing.Infrastructure.Storage +{ + public class TaskCache : ITaskCache + { + private MemoryCache _cache { get; } = MemoryCache.Default; + private CacheItemPolicy _defaultPolicy { get; } = new CacheItemPolicy(); + + public async Task AddOrGetExisting(string key, Func> valueFactory) + { + + var asyncLazyValue = new AsyncLazy(valueFactory); + var existingValue = (AsyncLazy)_cache.AddOrGetExisting(key, asyncLazyValue, _defaultPolicy); + + if (existingValue != null) + { + asyncLazyValue = existingValue; + } + + try + { + var result = await asyncLazyValue; + + // The awaited Task has completed. Check that the task still is the same version + // that the cache returns (i.e. the awaited task has not been invalidated during the await). + if (asyncLazyValue != _cache.AddOrGetExisting(key, new AsyncLazy(valueFactory), _defaultPolicy)) + { + // The awaited value is no more the most recent one. + // Get the most recent value with a recursive call. + return await AddOrGetExisting(key, valueFactory); + } + return result; + } + catch (Exception) + { + // Task object for the given key failed with exception. Remove the task from the cache. + _cache.Remove(key); + // Re throw the exception to be handled by the caller. + throw; + } + } + + public void Invalidate(string key) + { + _cache.Remove(key); + } + + public bool Contains(string key) + { + return _cache.Contains(key); + } + + public void Clear() + { + // A snapshot of keys is taken to avoid enumerating collection during changes. + var keys = _cache.Select(i => i.Key).ToList(); + keys.ForEach(k => _cache.Remove(k)); + } + + public T Get(string key) + { + var existingValue = (AsyncLazy)_cache.Get(key); + return existingValue.Value.Result; + } + + public virtual List GetCache() + { + List list = new List(); + + foreach (var item in _cache) + { + list.Add((T)item.Value); + } + + return list; + } + } +} diff --git a/src/Managing.Infrastructure.Tests/EvmManagerTests.cs b/src/Managing.Infrastructure.Tests/EvmManagerTests.cs new file mode 100644 index 0000000..12902ee --- /dev/null +++ b/src/Managing.Infrastructure.Tests/EvmManagerTests.cs @@ -0,0 +1,379 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Common; +using Managing.Domain.Accounts; +using Managing.Domain.Evm; +using Managing.Domain.Trades; +using Managing.Infrastructure.Evm; +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Infrastructure.Evm.Models.Gmx; +using Managing.Infrastructure.Evm.Referentials; +using Managing.Infrastructure.Evm.Services; +using Managing.Infrastructure.Evm.Services.Gmx; +using Nethereum.Contracts; +using Nethereum.Contracts.Standards.ERC721.ContractDefinition; +using Nethereum.Web3; +using System.Numerics; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Tests; + + +public class EvmManagerTests +{ + private readonly IEvmManager _manager; + private readonly List _chains; + + public List Subgraphs; + public readonly string PublicAddress = ""; + + + public EvmManagerTests() + { + _manager = new EvmManager(Subgraphs); + _chains = ChainService.GetChains(); + } + + [Fact] + public void Should_construct_manager() + { + var manager = new EvmManager(Subgraphs); + Assert.IsType(manager); + } + + [Theory] + [InlineData("")] + public async void Should_get_address_balance(string address) + { + var balance = await _manager.GetAddressBalance(address); + Assert.IsType(balance); + Assert.True(balance > -1); + } + + // Connect to nft contract + [Theory] + [InlineData("0x17f4BAa9D35Ee54fFbCb2608e20786473c7aa49f")] + public async void Should_return_holder_list_for_nft_collection(string contract) + { + var holders = await _manager.GetContractHolders(contract, DateTime.UtcNow.AddDays(-30)); + Assert.IsType>(holders); + Assert.True(holders.Any()); + } + + [Theory] + [InlineData("0xa435530d50d7D17Fd9fc6E1c897Dbf7C08E12d35", "0x17f4BAa9D35Ee54fFbCb2608e20786473c7aa49f")] + public async void Should_return_event_transfer_nft(string owner, string contract) + { + var manager = new EvmManager(Subgraphs); + var holders = await manager.GetNftEvent(owner, contract); + Assert.IsType>>(holders); + Assert.True(holders.Any()); + } + + [Fact] + public async void Should_return_date_of_block() + { + var manager = new EvmManager(Subgraphs); + var date = await manager.GetBlockDate(38793245); + Assert.Equal(new DateTime(2022, 11, 17, 11, 15, 33), date); + } + + [Fact] + public void Should_verify_message_sign() + { + var message = "Message to sign"; + var address = "0x94618601FE6cb8912b274E5a00453949A57f8C1e"; + var privateKey = "0x7580e7fb49df1c861f0050fae31c2224c6aba908e116b8da44ee8cd927b990b0"; + + var manager = new EvmManager(Subgraphs); + + var signature = manager.SignMessage(message, privateKey); + var addressRecovered = manager.VerifySignature(signature, message); + + Assert.Equal(addressRecovered, address); + } + + [Fact] + public void Shoud_return_generated_evm_address() + { + var manager = new EvmManager(Subgraphs); + var keys = manager.GenerateAddress(); + + Assert.IsType<(string Key, string Secret)>(keys); + Assert.False(string.IsNullOrEmpty(keys.Key)); + Assert.False(string.IsNullOrEmpty(keys.Secret)); + } + + [Fact] + public void Should_return_correct_account_for_mnemo() + { + var mnemo = "twist enemy flame exchange summer roast beyond friend image pyramid topple need"; + var manager = new EvmManager(Subgraphs); + var publicAddress = "0x3aBAD913A70554f416944F1a4C0EAbF3BCAFB959"; + var address = manager.GetAddressFromMnemo(mnemo); + Assert.NotNull(address); + Assert.IsType(address); + Assert.Equal(publicAddress, address); + } + + [Theory] + //[InlineData("0x0425dEAb364E9121F7CA284129dA854FD5cF22eD", Constants.Chains.Ethereum)] + [InlineData("0x7002AE0Bae7fC67416230F025A32EfE086C0934E", Constants.Chains.Arbitrum)] + public async void Should_return_balances(string publicAddress, string chainName) + { + var manager = new EvmManager(Subgraphs); + var chain = _chains.First(c => c.Name == chainName); + var balances = await manager.GetBalances(chain, 0, 30, publicAddress); + + Assert.IsType>(balances); + Assert.NotEmpty(balances); + } + + [Theory] + //[InlineData("0x7002ae0bae7fc67416230f025a32efe086c0934e", Constants.Chains.Arbitrum)] + [InlineData("0xc62F5499789b716Aa94a421A60c76c8c13A31ab6", Constants.Chains.Ethereum)] + public async void Should_return_all_balance(string publicAddress, string chainName) + { + var manager = new EvmManager(Subgraphs); + var chain = _chains.First(c => c.Name == chainName); + var balances = await manager.GetAllBalances(chain, publicAddress); + + Assert.IsType>(balances); + Assert.True(balances.Count > 1); + } + + [Theory] + [InlineData("", Constants.Chains.Arbitrum, Ticker.GMX)] + public async void Should_return_token_balance(string publicAddress, string chainName, Ticker ticker) + { + var manager = new EvmManager(Subgraphs); + var balance = await manager.GetTokenBalance(chainName, ticker, publicAddress); + + Assert.IsType(balance); + Assert.True(balance.Balance > 0); + } + + [Theory] + [InlineData("")] + public async void Should_return_balance_of_ethers(string publicAddress) + { + var manager = new EvmManager(Subgraphs); + var chain = _chains.First(c => c.Name == Constants.Chains.Ethereum); + var balance = await manager.GetEtherBalance(chain, publicAddress); + + Assert.IsType(balance); + } + + [Theory] + [InlineData("")] + public async void Should_return_all_balance_for_all_chain(string publicAddress) + { + var manager = new EvmManager(Subgraphs); + var balances = await manager.GetAllBalancesOnAllChain(publicAddress); + + Assert.IsType>(balances); + Assert.True(balances.Count > 0); + } + + [Theory] + [InlineData(Ticker.BTC, Timeframe.FiveMinutes)] + public async void Get_Prices(Ticker ticker, Timeframe timeframe) + { + var manager = new EvmManager(Subgraphs); + var candles = await manager.GetCandles(SubgraphProvider.ChainlinkPrice, ticker, DateTime.UtcNow, timeframe); + + if (candles == null || !candles.Any()) + { + candles = await manager.GetCandles(SubgraphProvider.ChainlinkGmx, ticker, DateTime.UtcNow, timeframe); + } + Assert.NotNull(candles); + Assert.True(candles.Any()); + } + + [Fact] + public async void Get_Available_Tickers() + { + var manager = new EvmManager(Subgraphs); + var tickers = await manager.GetAvailableTicker(); + + Assert.NotEmpty(tickers); + } + + [Fact] + public async void GetLastCandle() + { + var manager = new EvmManager(Subgraphs); + var candle = await manager.GetCandle(SubgraphProvider.Gbc, Ticker.BTC); + + Assert.NotNull(candle); + } + + [Fact] + public async void Should_Init_Address_For_Trading() + { + var manager = new EvmManager(Subgraphs); + var accountInitilized = await manager.InitAddress(Constants.Chains.Arbitrum, PublicAddress, "PrivateKey"); + + Assert.True(accountInitilized); + } + + [Fact] + public async void Should_send_eth_from_account() + { + var manager = new EvmManager(Subgraphs); + var chain = _chains.First(c => c.Name == Constants.Chains.Arbitrum); + var balance = await manager.GetEtherBalance(chain, PublicAddress); + // Update receiver + var receiverAddress = ""; + var sendResult = await manager.Send( + chain, + Ticker.ETH, + balance.Balance / 2, + PublicAddress, + "", + receiverAddress); + + Assert.True(sendResult); + } + + [Fact] + public async void Should_send_Gmx_from_account() + { + var manager = new EvmManager(Subgraphs); + var chain = _chains.First(c => c.Name == Constants.Chains.Arbitrum); + var receiverAddress = ""; + var balance = await manager.GetTokenBalance(chain.Name, Ticker.GMX, PublicAddress); + + var sendResult = await manager.Send( + chain, + Ticker.GMX, + balance.Balance / 2, + PublicAddress, + "", + receiverAddress); + + Assert.True(sendResult); + } + + [Fact] + public async void Should_return_indexes_from_gmx() + { + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(chain.RpcUrl); + var indexes = await GmxService.GetLastIndex(web3, ""); + + Assert.IsType(indexes); + } + + [Theory] + [InlineData("")] + public async void Should_return_gmx_orders(string publicAddress) + { + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(chain.RpcUrl); + var orders = await GmxService.GetOrders(web3, publicAddress, Ticker.BTC); + + Assert.IsType>(orders); + } + + [Fact] + public async void Should_return_orders() + { + var manager = new EvmManager(Subgraphs); + var account = GetAccount(); + + var orders = await manager.GetOrders(account, Ticker.BTC); + + Assert.IsType>(orders); + } + + [Fact] + public async void Should_cancel_gmx_orders() + { + var manager = new EvmManager(Subgraphs); + var account = GetAccount(); + + var cancelled = await manager.CancelOrders(account, Ticker.BTC); + + Assert.IsType(cancelled); + } + + private static Account GetAccount() + { + return new Account + { + Key = "PublicAddress", + Secret = "PrivateKey" + }; + } + + [Fact] + public void Should_convert_quantity() + { + var quantity = Web3.Convert.ToWei(0.0019); + + Assert.IsType(quantity); + } + + [Fact] + public async void Should_approve_order() + { + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(chain.RpcUrl); + var approval = await GmxService.ApproveOrder(web3, Ticker.BTC, PublicAddress, 0.0003m); + + Assert.IsType(approval); + } + + [Fact] + public async void Should_check_approved_gmx_plugin() + { + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(chain.RpcUrl); + var isPluginAdded = await GmxService.IsPluginAdded(web3, "", Arbitrum.Address.OrderBook); + + Assert.IsType(isPluginAdded); + Assert.True(isPluginAdded); + } + + [Fact] + public void Should_return_correct_acceptable_price() + { + var acceptablePrice = GmxHelpers.GetAcceptablePrice(16672.76m, true); + var price = new BigInteger(1662274172); + var expected = Web3.Convert.ToWei(price, 25); + + Assert.NotNull(acceptablePrice); + Assert.IsType(acceptablePrice); + Assert.Equal(expected, acceptablePrice); + } + + [Fact] + public async void Should_return_quantity_in_position() + { + var manager = new EvmManager(Subgraphs); + var quantity = await manager.QuantityInPosition(Constants.Chains.Arbitrum, PublicAddress, Ticker.BTC); + + Assert.NotNull(quantity); + } + + [Fact] + public async void Should_return_Gmx_position() + { + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(chain.RpcUrl); + var position = await GmxService.GetGmxPosition(web3, "", Ticker.BTC); + + Assert.IsType(position); + } + + [Fact] + public async void Should_return_Trade() + { + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(chain.RpcUrl); + var position = await GmxService.GetTrade(web3, "", Ticker.ETH); + + Assert.IsType(position); + } +} diff --git a/src/Managing.Infrastructure.Tests/ExchangeServicesTests.cs b/src/Managing.Infrastructure.Tests/ExchangeServicesTests.cs new file mode 100644 index 0000000..7a6bdfa --- /dev/null +++ b/src/Managing.Infrastructure.Tests/ExchangeServicesTests.cs @@ -0,0 +1,200 @@ +using Managing.Common; +using Managing.Domain.Trades; +using Managing.Infrastructure.Exchanges; +using Microsoft.Extensions.Logging; +using Managing.Domain.Candles; +using Xunit; +using static Managing.Common.Enums; +using Managing.Domain.Accounts; +using Moq; +using Managing.Application.Abstractions.Repositories; +using Managing.Application.Abstractions.Services; +using Managing.Infrastructure.Exchanges.Abstractions; +using Managing.Infrastructure.Exchanges.Exchanges; +using Managing.Infrastructure.Evm; +using Ticker = Managing.Common.Enums.Ticker; +using Managing.Infrastructure.Evm.Abstractions; + +namespace Managing.Infrastructure.Tests +{ + public class ExchangeServicesTests + { + private readonly IExchangeService _exchangeService; + public readonly string PublicAddress = ""; + + public List Subgraphs; + + public ExchangeServicesTests() + { + + ILoggerFactory doesntDoMuch = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory(); + var candleRepository = new Mock().Object; + var evmManager = new EvmManager(Subgraphs); + var evmProcessor = new EvmProcessor(new Mock>().Object, evmManager); + var exchangeProcessors = new List() + { + evmProcessor + }; + + _exchangeService = new ExchangeService(doesntDoMuch.CreateLogger(), candleRepository, exchangeProcessors); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC)] + public void Should_Return_Price_For_Given_Ticker(Enums.TradingExchanges exchange, Ticker ticker) + { + var account = GetAccount(exchange); + var price = _exchangeService.GetPrice(account, ticker, DateTime.Now); + Assert.IsType(price); + Assert.InRange(price, 0, 1000000); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.ADA)] + public void Should_Return_Candle_For_Given_Ticker(Enums.TradingExchanges exchange, Ticker ticker) + { + var account = GetAccount(exchange); + var candle = _exchangeService.GetCandle(account, ticker, DateTime.Now); + Assert.IsType(candle); + Assert.InRange(candle.High, 0, 1000000); + Assert.InRange(candle.Low, 0, 1000000); + Assert.InRange(candle.Open, 0, 1000000); + Assert.InRange(candle.Close, 0, 1000000); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm)] + public void Should_Return_Balance(Enums.TradingExchanges exchange) + { + var account = GetAccount(exchange); + var balance = _exchangeService.GetBalance(account).Result; + Assert.IsType(balance); + Assert.True(balance >= 0); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, "0x2875673415c66bf05091eeff3887e0d40136d5ea443a4e63e7f4e41a6580575e", Ticker.BTC)] + public void Should_Return_Trade_For_Given_OrderId(Enums.TradingExchanges exchange, string orderId, Ticker ticker) + { + var account = GetAccount(exchange); + var trade = _exchangeService.GetTrade(account, orderId, ticker).Result; + Assert.IsType(trade); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC)] + public void Should_Return_List_Of_Candle_Given_Ticker(Enums.TradingExchanges exchange, Ticker ticker) + { + var account = GetAccount(exchange); + var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(-10), Timeframe.OneDay).Result; + Assert.IsType>(candles); + Assert.InRange(candles.Count, 1, 15); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.ADA, "7INRiu79cv2nCONNlILPu0")] + public void Should_Return_Long_Trade(Enums.TradingExchanges exchange, Ticker ticker, string exchangeOrderId) + { + var account = GetAccount(exchange); + var trade = _exchangeService.GetTrade(account, exchangeOrderId, ticker).Result; + Assert.IsType(trade); + Assert.True(trade.Direction == TradeDirection.Long); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.ADA, "AQKzJpDNrfVjuq81baPLfR")] + public void Should_Return_Short_Trade(Enums.TradingExchanges exchange, Ticker ticker, string exchangeOrderId) + { + var account = GetAccount(exchange); + var trade = _exchangeService.GetTrade(account, exchangeOrderId, ticker).Result; + Assert.IsType(trade); + Assert.True(trade.Direction == TradeDirection.Short); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC)] + public async void Should_Return_Balance_For_Ticker(Enums.TradingExchanges exchange, Ticker ticker) + { + var account = GetAccount(exchange); + var balance = await _exchangeService.GetQuantityInPosition(account, ticker); + Assert.IsType(balance); + Assert.True(balance > 0); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.ADA)] + public void Should_Return_Trade_List_For_Ticker(Enums.TradingExchanges exchange, Ticker ticker) + { + var account = GetAccount(exchange); + var trades = _exchangeService.GetTrades(account, ticker).Result; + Assert.IsType>(trades); + Assert.True(trades.Count > 0); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm)] + public void Should_Return_Fee(Enums.TradingExchanges exchange) + { + var account = GetAccount(exchange); + var fee = _exchangeService.GetFee(account); + Assert.IsType(fee); + Assert.True(fee > 0); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC)] + public void Should_Return_Volume(Enums.TradingExchanges exchange, Ticker ticker) + { + var account = GetAccount(exchange); + var volume = _exchangeService.GetVolume(account, ticker); + Assert.IsType(volume); + Assert.True(volume > 0); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC)] + public async void Should_Return_Open_Order(Enums.TradingExchanges exchange, Ticker ticker) + { + var account = GetAccount(exchange); + var trades = await _exchangeService.GetOpenOrders(account, ticker); + Assert.IsType>(trades); + } + + [Theory] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC, 0.1, TradeDirection.Long)] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC, 700, TradeDirection.Long)] + [InlineData(Enums.TradingExchanges.Evm, Ticker.BTC, 700, TradeDirection.Short)] + public void Should_Return_Best_Price( + Enums.TradingExchanges exchange, + Ticker ticker, + decimal quantity, + TradeDirection direction) + { + var account = GetAccount(exchange); + var lastPrice = _exchangeService.GetPrice(account, ticker, DateTime.UtcNow); + var bestPrice = _exchangeService.GetBestPrice(account, ticker, lastPrice, quantity, direction); + + Assert.IsType(bestPrice); + + var percentageDiff = ( (bestPrice * 100) / lastPrice) - 100; + Assert.True(Math.Abs(percentageDiff) < 1); + } + + private Account GetAccount(Enums.TradingExchanges exchange) + { + var account = new Account(); + switch (exchange) + { + case Enums.TradingExchanges.Evm: + account.Exchange = Enums.TradingExchanges.Evm; + account.Key = PublicAddress; + account.Secret = "PrivateKey"; + account.Name = "EvmAccount"; + break; + default: + break; + } + return account; + } + } +} diff --git a/src/Managing.Infrastructure.Tests/Managing.Infrastructure.Tests.csproj b/src/Managing.Infrastructure.Tests/Managing.Infrastructure.Tests.csproj new file mode 100644 index 0000000..19517b9 --- /dev/null +++ b/src/Managing.Infrastructure.Tests/Managing.Infrastructure.Tests.csproj @@ -0,0 +1,27 @@ + + + + net7.0 + enable + AnyCPU;x64 + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/src/Managing.Infrastructure.Tests/SubgraphTests.cs b/src/Managing.Infrastructure.Tests/SubgraphTests.cs new file mode 100644 index 0000000..ff2040d --- /dev/null +++ b/src/Managing.Infrastructure.Tests/SubgraphTests.cs @@ -0,0 +1,56 @@ +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Tests; + +public class SubgraphTests +{ + public SubgraphTests() + { + } + + // [Fact] + // public async void Should_get_price_from_chainlink_gmx() + // { + // var prices = await ChainlinkGmx.GetPrices(Ticker.BTC, DateTime.UtcNow.AddDays(-4), Timeframe.FiveMinutes); + + // Assert.NotNull(prices); + // Assert.True(prices.Any()); + // } + + // [Fact] + // public async void Should_get_price_from_gbc() + // { + // var prices = await GbcFeed.GetPrices(Ticker.BTC, DateTime.UtcNow.AddDays(-15), Timeframe.FiveMinutes); + + // Assert.NotNull(prices); + // Assert.True(prices.Any()); + // } + + // [Fact] + // public async void Should_get_price_from_chainlink() + // { + // var prices = await Chainlink.GetPrices(Ticker.BTC, DateTime.UtcNow.AddDays(-4), Timeframe.FiveMinutes); + + // Assert.NotNull(prices); + // Assert.True(prices.Any()); + // } + + // [Fact] + // public async void Should_get_top_tokens() + // { + // var top = await Uniswap.GetTopTokens(); + + // Assert.NotNull(top); + // Assert.True(top.Tokens.Any()); + // } + + // [Fact] + // public async void Should_get_available_pairs_for_chainlink() + // { + // var pairs = await Chainlink.GetTickers(); + + // Assert.NotNull(pairs); + // Assert.True(pairs.Any()); + // } +} diff --git a/src/Managing.Infrastructure.Tests/TradaoTests.cs b/src/Managing.Infrastructure.Tests/TradaoTests.cs new file mode 100644 index 0000000..b81ff8f --- /dev/null +++ b/src/Managing.Infrastructure.Tests/TradaoTests.cs @@ -0,0 +1,24 @@ +using Managing.Domain.Shared.Helpers; +using Managing.Infrastructure.Evm.Services; +using Xunit; + +namespace Managing.Infrastructure.Tests; + +public class TradaoTests +{ + [Fact] + public async void Should_return_best_trader() + { + var service = new TradaoService(); + var details = await service.GetBestTrader(); + Assert.NotNull(details); + } + + [Fact] + public async void Should_return_bad_trader() + { + var service = new TradaoService(); + var details = (await service.GetBadTrader()).FindBadTrader(); + Assert.NotNull(details); + } +} diff --git a/src/Managing.Infrastructure.Web3/Abstractions/ISubgraphPrices.cs b/src/Managing.Infrastructure.Web3/Abstractions/ISubgraphPrices.cs new file mode 100644 index 0000000..3cc1208 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Abstractions/ISubgraphPrices.cs @@ -0,0 +1,12 @@ +using Managing.Domain.Candles; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Abstractions; + +public interface ISubgraphPrices +{ + public SubgraphProvider GetProvider(); + Task> GetPrices(Ticker ticker, DateTime startDate, Timeframe timeframe); + Task GetVolume(Ticker ticker); + Task> GetTickers(); +} diff --git a/src/Managing.Infrastructure.Web3/Abstractions/IUniswap.cs b/src/Managing.Infrastructure.Web3/Abstractions/IUniswap.cs new file mode 100644 index 0000000..2523915 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Abstractions/IUniswap.cs @@ -0,0 +1,9 @@ +using Managing.Infrastructure.Evm.Subgraphs.Models; + +namespace Managing.Infrastructure.Evm.Abstractions; + +public interface IUniswap : ISubgraphPrices +{ + Task GetMostLiquidMarketPairs(); + Task GetTopTokens(); +} diff --git a/src/Managing.Infrastructure.Web3/EvmManager.cs b/src/Managing.Infrastructure.Web3/EvmManager.cs new file mode 100644 index 0000000..2a7c6bd --- /dev/null +++ b/src/Managing.Infrastructure.Web3/EvmManager.cs @@ -0,0 +1,620 @@ +using Managing.Application.Abstractions.Repositories; +using Managing.Domain.Evm; +using NBitcoin; +using Nethereum.Contracts; +using Nethereum.Hex.HexTypes; +using Nethereum.Signer; +using Nethereum.Web3; +using Nethereum.HdWallet; +using System.Numerics; +using System.Net.Http.Json; +using Managing.Infrastructure.Evm.Services; +using Managing.Domain.Candles; +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Core; +using static Managing.Common.Enums; +using Managing.Infrastructure.Evm.Services.Gmx; +using Nethereum.Contracts.Standards.ERC20.ContractDefinition; +using Managing.Common; +using Managing.Domain.Trades; +using Managing.Domain.Accounts; +using Managing.Infrastructure.Evm.Models.Gmx; +using Managing.Infrastructure.Evm.Referentials; + +namespace Managing.Infrastructure.Evm; + +public class EvmManager : IEvmManager +{ + private readonly Web3 _web3; + private readonly HttpClient _httpClient; + private readonly string _password = "!StrongPassword94"; + private readonly IEnumerable _subgraphs; + private Dictionary> _geckoPrices; + + public EvmManager(IEnumerable subgraphs) + { + var defaultChain = ChainService.GetEthereum(); + _web3 = new Web3(defaultChain.RpcUrl); + _httpClient = new HttpClient(); + _subgraphs = subgraphs; + _geckoPrices = _geckoPrices != null && _geckoPrices.Any() ? _geckoPrices : new Dictionary>(); + SetupPrices(); + } + + public void SetupPrices() + { + try + { + var geckoIds = new List(); + + foreach (var ticker in Enum.GetValues()) + { + var geckoId = TokenService.GetGeckoToken(ticker.ToString())?.Id; + if (geckoId != null) + { + geckoIds.Add(geckoId); + } + } + + if (geckoIds != null && geckoIds.Count > 0 && !_geckoPrices.Any()) + { + _geckoPrices = GetPrices(geckoIds).Result; + } + } + catch (Exception ex) + { + // TODO : Handle error + } + } + + public async Task GetAddressBalance(string address) + { + var balance = await _web3.Eth.GetBalance.SendRequestAsync(address); + var etherAmount = Web3.Convert.FromWei(balance.Value); + return etherAmount; + } + + public async Task> GetContractHolders(string contractAddress, DateTime since) + { + var holders = new List(); + var contract = _web3.Eth.ERC721.GetContractService(contractAddress); + + // Retrieve total supply to iterate over all token id generated within the contract + var totalSupply = await contract.TotalSupplyQueryAsync(); + + for (int tokenId = 1; tokenId < 10; tokenId++) + { + // Retrieve the owner of the nft + var tokenOwner = await contract.OwnerOfQueryAsync(tokenId); + + // If holder already have an nft we get the holder + // Otherwise we create a new holder + var holder = holders.FirstOrDefault(h => h.HolderAddress == tokenOwner) ?? new Holder(tokenOwner); + + // Retrieve all events related to the owner on the contract address + var nfts = await GetNftEvent(contractAddress, tokenOwner); + + // Get tokenId related event + var nftEvent = nfts.FirstOrDefault(n => n.Event.TokenId == new BigInteger(tokenId)); + + if (nftEvent != null) + { + // Retrieve the date of the nft event that occur in the blocknumber + var blockDate = await GetBlockDate(nftEvent.Log.BlockNumber); + var nft = new Nft(contractAddress, tokenId, blockDate); + + // Verify if the date of the holding is before the date passed from the parameters + if (blockDate <= since) + { + holder.AddNft(nft); + + // If holder do not exist in the list we add it + if (!holders.Exists(h => h.HolderAddress == tokenOwner)) + { + holders.Add(holder); + } + } + else + { + Console.WriteLine($"TokenId #{tokenId} for owner {tokenOwner} date not in range ({blockDate:f})"); + } + } + else + { + Console.WriteLine($"Error when getting tokenId #{tokenId} for owner {tokenOwner}"); + } + + UpdateLine(tokenId, (int)totalSupply, holders.Count); + } + + return holders; + } + + public async Task>> GetNftEvent(string contractAddress, string tokenOwner) + { + return await NftService.GetNftEvent(_web3, tokenOwner, contractAddress); + } + + public async Task GetBlockDate(int blockNumber) + { + return await GetBlockDate(new HexBigInteger(blockNumber)); + } + + public async Task GetBlockDate(HexBigInteger blockNumber) + { + var block = await _web3.Eth.Blocks.GetBlockWithTransactionsByNumber.SendRequestAsync(blockNumber); + var date = DateHelpers.GetFromUnixTimestamp((int)block.Timestamp.Value); + return date; + } + + private void UpdateLine(int count, int total, int holders) + { + Console.WriteLine($"{count}/{total} - {(((decimal)count * 100m) / (decimal)total)} % - Holders : {holders}"); + } + + public string VerifySignature(string signature, string message) + { + var signer = new EthereumMessageSigner(); + var addressRecovered = signer.EncodeUTF8AndEcRecover(message, signature); + return addressRecovered; + } + + public string SignMessage(string message, string privateKey) + { + var signer = new EthereumMessageSigner(); + var signature = signer.EncodeUTF8AndSign(message, new EthECKey(privateKey)); + return signature; + } + + public (string Key, string Secret) GenerateAddress() + { + var mnemo = new Mnemonic(Wordlist.English, WordCount.Twelve); + var wallet = new Wallet(mnemo.ToString(), _password); + var account = wallet.GetAccount(0); + + return (account.Address, mnemo.ToString()); + } + + public string GetAddressFromMnemo(string mnemo) + { + var wallet = new Wallet(mnemo, _password); + return wallet.GetAccount(0).Address; + } + + public async Task GetEtherBalance(Domain.Evm.Chain chain, string account) + { + var web3 = new Web3(chain.RpcUrl); + var etherBalance = Web3.Convert.FromWei(await web3.Eth.GetBalance.SendRequestAsync(account)); + var etherPrice = (await GetPrices(new List { "ethereum"}))["ethereum"]["usd"]; + + return new EvmBalance() { Balance = etherBalance, Price = etherPrice, TokenName = "ETH", Value = etherBalance * etherPrice }; + } + + public async Task> GetAllBalances(Domain.Evm.Chain chain, string publicAddress) + { + var balances = new List(); + + + var web3 = new Web3(chain.RpcUrl); + SetupPrices(); + foreach (var ticker in Enum.GetValues()) + { + try + { + var balance = await GetTokenBalance(chain.Name, ticker, publicAddress); + if (balance != null && balance.Balance > 0) + { + balances.Add(balance); + } + } + catch (Exception ex) + { + // TODO : handle exception + } + } + var etherBalance = await GetEtherBalance(chain, publicAddress); + etherBalance.Chain = chain; + balances.Add(etherBalance); + //var pageSize = 50; + //var tokenCount = TokenService.GetTokens().Count; + //for (int i = 0; i < (tokenCount / pageSize); i++) + //{ + // var pageBalances = await GetBalances(chain, i, pageSize, publicAddress).ConfigureAwait(false); + // balances.AddRange(pageBalances); + //} + + return balances; + } + + public async Task GetTokenBalance(string chainName, Ticker ticker, string publicAddress) + { + var chain = ChainService.GetChain(chainName); + var web3 = new Web3(chain.RpcUrl); + var balanceOfMessage = new BalanceOfFunction() { Owner = publicAddress }; + + //Creating a new query handler + var queryHandler = web3.Eth.GetContractQueryHandler(); + + var contractAddress = TokenService.GetContractAddress(ticker); + + if (contractAddress == Arbitrum.Address.Zero) + return null; + + var balance = await queryHandler + .QueryAsync(contractAddress, balanceOfMessage) + .ConfigureAwait(false); + + var geckoId = TokenService.GetGeckoToken(ticker.ToString())?.Id; + + if (geckoId == null) + return null; + + var tokenUsdPrice = _geckoPrices[geckoId][Constants.Stablecoins.Usd.ToLowerInvariant()]; + var tokenDecimal = TokenService.GetDecimal(ticker); + var balanceFromWei = Web3.Convert.FromWei(balance, tokenDecimal); + + var evmBalance = new EvmBalance + { + TokenName = ticker.ToString(), + Balance = balanceFromWei, + TokenAddress = contractAddress, + Value = tokenUsdPrice * balanceFromWei, + Price = tokenUsdPrice, + Chain = chain + }; + + return evmBalance; + } + + public async Task> GetBalances(Domain.Evm.Chain chain, int page, int pageSize, string publicAddress) + { + var callList = new List(); + var startItem = (page * pageSize); + var tokens = TokenService.GetTokens(); + var totaItemsToFetch = startItem + pageSize <= tokens.Count ? startItem + pageSize : tokens.Count + startItem; + + for (int i = startItem; i < totaItemsToFetch; i++) + { + var balanceOfMessage = new BalanceOfFunction() { Owner = publicAddress }; + var call = new MulticallInputOutput(balanceOfMessage, + tokens[i].Address); + callList.Add(call); + } + var evmTokens = new List<(EvmBalance Balance, GeckoToken GeckoToken)>(); + + try + { + var web3 = new Web3(chain.RpcUrl); + var geckoTokens = TokenService.GetGeckoTokens(); + + await web3.Eth.GetMultiQueryHandler().MultiCallAsync(callList.ToArray()); + + for (int i = startItem; i < totaItemsToFetch; i++) + { + var balance = ((MulticallInputOutput)callList[i - startItem]).Output.Balance; + if (balance > 0) + { + var tokenBalance = new EvmBalance() + { + Balance = Web3.Convert.FromWei(balance, tokens[i].Decimals), + TokenName = tokens[i].Symbol, + TokenAddress = tokens[i].Address, + Chain = chain + }; + + var geckoToken = geckoTokens.FirstOrDefault(x => string.Equals(x.Symbol, tokens[i].Symbol, StringComparison.InvariantCultureIgnoreCase)); + + evmTokens.Add((tokenBalance, geckoToken)); + } + } + + if (evmTokens.Count > 0) + { + var ids = evmTokens.Select(x => x.GeckoToken?.Id).Distinct().ToList(); + var prices = await GetPrices(ids).ConfigureAwait(false); + + foreach (var balance in evmTokens) + { + if (balance.GeckoToken != null) + { + var price = prices[balance.GeckoToken.Id.ToLower()]; + balance.Balance.Price = price["usd"]; + balance.Balance.Value = balance.Balance.Price * balance.Balance.Balance; + } + } + } + + } + catch (Exception ex) + { + // TODO : Handle error + // No enable to reach rpc + } + + return evmTokens.Select(e => e.Balance).ToList(); + } + + public async Task>> GetPrices(List geckoIds) + { + var idsCombined = string.Join(",", geckoIds); + return await _httpClient.GetFromJsonAsync>>("https://api.coingecko.com/api/v3/simple/price?ids=" + idsCombined + "&vs_currencies=usd"); + } + + public async Task> GetAllBalancesOnAllChain(string publicAddress) + { + var chainBalances = new List(); + var chains = ChainService.GetChains(); + + foreach (var chain in chains) + { + chainBalances.AddRange(await GetAllBalances(chain, publicAddress)); + } + + return chainBalances; + } + + public async Task> GetCandles(SubgraphProvider subgraphProvider, Ticker ticker, DateTime startDate, Timeframe timeframe) + { + + string gmxTimeframe = GmxHelpers.GeTimeframe(timeframe); + var gmxPrices = await _httpClient.GetFromJsonAsync($"https://stats.gmx.io/api/candles/{ticker}?preferableChainId=42161&period={gmxTimeframe}&from={startDate.ToUnixTimestamp()}&preferableSource=fast"); + //var subgraph = _subgraphs.First(s => s.GetProvider() == subgraphProvider); + //var prices = await subgraph.GetPrices(ticker, startDate, timeframe); + + //if (prices == null) + //{ + // foreach (var subgraphFallback in _subgraphs.Where(s => s.GetProvider() != subgraphProvider)) + // { + // prices = await subgraphFallback.GetPrices(ticker, startDate, timeframe); + + // if (prices != null) + // break; + // } + //} + + if (gmxPrices == null) + return null; + + gmxPrices.prices.RemoveAt(gmxPrices.prices.Count - 1); + return gmxPrices.prices.Select(p => GmxMappers.Map(p, ticker, timeframe)).ToList(); + } + + public decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker) + { + var subgraph = GetSubgraph(subgraphProvider); + var volume = subgraph.GetVolume(ticker).Result; + return volume; + } + + private ISubgraphPrices GetSubgraph(SubgraphProvider subgraphProvider) + { + return _subgraphs.First(s => s.GetProvider() == subgraphProvider); + } + + public async Task> GetAvailableTicker() + { + var subgraph = GetSubgraph(SubgraphProvider.Gbc); + var pairs = await subgraph.GetTickers(); + return pairs.ToList(); + } + + public async Task GetCandle(SubgraphProvider subgraphProvider, Ticker ticker) + { + var lastPrices = await GetCandles(subgraphProvider, ticker, DateTime.UtcNow.AddMinutes(-15), Timeframe.FiveMinutes); + return lastPrices.Last(); + } + + public async Task InitAddress(string chainName, string publicAddress, string privateKey) + { + try + { + var chain = ChainService.GetChain(chainName); + var account = new Wallet(privateKey, _password).GetAccount(publicAddress); + var web3 = new Web3(account, chain.RpcUrl); + var tickers = await GetAvailableTicker(); + await GmxService.InitAccountForTrading(web3, publicAddress, tickers); + return true; + } + catch (Exception ex) + { + return false; + } + } + + public async Task ApproveTicker(string publicAddress, string privateKey, Ticker ticker) + { + try + { + var account = new Wallet(privateKey, _password).GetAccount(publicAddress); + var contractAddress = TokenService.GetContractAddress(ticker); + await GmxService.ApproveToken(_web3, publicAddress, contractAddress); + return true; + } + catch (Exception ex) + { + return false; + } + } + + public async Task Send( + Domain.Evm.Chain chain, + Ticker ticker, + decimal amount, + string publicAddress, + string privateKey, + string receiverAddress) + { + var account = new Wallet(privateKey, _password).GetAccount(publicAddress); + var web3 = new Web3(account, chain.RpcUrl); + + try + { + if (ticker == Ticker.ETH) + { + return await SendEth(amount, receiverAddress, web3); + } + else + { + return await SendToken(ticker, amount, publicAddress, receiverAddress, web3); + } + } + catch (Exception ex) + { + return false; + } + } + + private static async Task SendEth(decimal amount, string receiverAddress, Web3 web3) + { + web3.TransactionManager.UseLegacyAsDefault = true; + var ethService = web3.Eth.GetEtherTransferService(); + var gas = await ethService.EstimateGasAsync(receiverAddress, amount); + var transaction = await ethService.TransferEtherAndWaitForReceiptAsync(receiverAddress, amount, gas: gas); + + return transaction.Status.Value == 1; + } + + private static async Task SendToken( + Ticker ticker, + decimal amount, + string senderAddress, + string receiverAddress, + Web3 web3) + { + + var contractAddress = TokenService.GetContractAddress(ticker); + var transactionMessage = new TransferFunction + { + FromAddress = senderAddress, + To = receiverAddress, + Value = Web3.Convert.ToWei(amount) + }; + + var transferHandler = web3.Eth.GetContractTransactionHandler(); + var transferReceipt = + await transferHandler.SendRequestAndWaitForReceiptAsync(contractAddress, transactionMessage); + + var transaction = await web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(transferReceipt.TransactionHash); + return transaction != null; + } + + public async Task CancelOrders(Account account, Ticker ticker) + { + var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key); + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(wallet, chain.RpcUrl); + return await GmxService.CancelOrders(web3, account.Key, ticker); + } + + public async Task IncreasePosition( + Account account, + Ticker ticker, + TradeDirection direction, + decimal price, + decimal quantity, + decimal? leverage) + { + var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key); + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(wallet, chain.RpcUrl); + + Trade trade = null; + try + { + trade = await GmxService.IncreasePosition(web3, account.Key, ticker, direction, price, quantity, leverage); + + } + catch (Exception ex) + { + throw; + } + + return trade; + } + + public async Task DecreasePosition( + Account account, + Ticker ticker, + TradeDirection direction, + decimal price, + decimal quantity, + decimal? leverage) + { + var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key); + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(wallet, chain.RpcUrl); + + Trade trade = null; + try + { + trade = await GmxService.DecreasePosition(web3, account.Key, ticker, direction, price, quantity, leverage); + } + catch (Exception ex) + { + throw; + } + + return trade; + } + + public async Task DecreaseOrder(Account account, TradeType tradeType, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage) + { + + var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key); + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(wallet, chain.RpcUrl); + + Trade trade = null; + try + { + trade = await GmxService.DecreaseOrder(web3, tradeType, account.Key, ticker, direction, price, quantity, leverage); + } + catch (Exception ex) + { + throw; + } + + return trade; + } + + public async Task GetTrade(Account account, string chainName, Ticker ticker) + { + return await GetTrade(account.Key, chainName, ticker); + } + + + public async Task GetTrade(string reference, string chainName, Ticker ticker) + { + var chain = ChainService.GetChain(chainName); + var web3 = new Web3(chain.RpcUrl); + return await GmxService.GetTrade(web3, reference, ticker); + } + + public async Task QuantityInPosition(string chainName, string publicAddress, Ticker ticker) + { + var chain = ChainService.GetChain(chainName); + var web3 = new Web3(chain.RpcUrl); + var quantity = await GmxService.QuantityInPosition(web3, publicAddress, ticker); + return quantity; + } + + public async Task GetFee(string chainName) + { + var chain = ChainService.GetChain(chainName); + var web3 = new Web3(chain.RpcUrl); + var etherPrice = (await GetPrices(new List { "ethereum" }))["ethereum"]["usd"]; + var fee = await GmxService.GetFee(web3, etherPrice); + return fee; + } + + public async Task> GetOrders(Account account, Ticker ticker) + { + var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key); + var chain = ChainService.GetChain(Constants.Chains.Arbitrum); + var web3 = new Web3(wallet, chain.RpcUrl); + var orders = await GmxService.GetOrders(web3, account.Key, ticker); + + return GmxHelpers.Map(orders, ticker); + } + +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Web3/Extensions/PriceExtensions.cs b/src/Managing.Infrastructure.Web3/Extensions/PriceExtensions.cs new file mode 100644 index 0000000..79b312c --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Extensions/PriceExtensions.cs @@ -0,0 +1,75 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Infrastructure.Evm.Subgraphs.Models; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Extensions; + +public static class PriceExtensions +{ + public static List GetCandles(this Round[] prices, Timeframe timeframe, Ticker ticker) + { + int timezoneOffset = - (int)(new DateTimeOffset(DateTime.UtcNow).Offset.TotalSeconds); + var CHART_PERIODS = new Dictionary + { + { Timeframe.FiveMinutes, 60 * 5 }, + { Timeframe.FifteenMinutes, 60 * 15 }, + { Timeframe.OneHour, 60 * 60 }, + { Timeframe.FourHour, 60 * 60 * 4 }, + { Timeframe.OneDay, 60 * 60 * 24 } + }; + int periodTime = CHART_PERIODS[timeframe]; + + if (prices.Count() < 2) + { + return new List(); + } + + List candles = new List(); + Round first = prices[0]; + int prevTsGroup = (int)Math.Floor((decimal)first.UnixTimestamp / periodTime) * periodTime; + decimal prevPrice = decimal.Parse(first.Value); + decimal o = prevPrice; + decimal h = prevPrice; + decimal l = prevPrice; + decimal c = prevPrice; + for (int i = 1; i < prices.Count(); i++) + { + var current = prices[i]; + int tsGroup = (int)Math.Floor((decimal)current.UnixTimestamp / periodTime) * periodTime; + if (prevTsGroup != tsGroup) + { + candles.Add(new Candle + { + OpenTime = DateHelpers.GetFromUnixTimestamp(prevTsGroup + timezoneOffset), + Date = DateHelpers.GetFromUnixTimestamp(tsGroup + timezoneOffset), + Open = o, + High = h, + Low = l, + Close = c, + Timeframe = timeframe + }); + o = c; + h = Math.Max(o, c); + l = Math.Min(o, c); + } + c = decimal.Parse(current.Value); + h = Math.Max(h, c); + l = Math.Min(l, c); + prevTsGroup = tsGroup; + } + + return candles.Select(x => new Candle + { + OpenTime = x.OpenTime, + Date = x.Date, + Open = x.Open, + Close = x.Close, + High = x.High, + Low = x.Low, + Timeframe = x.Timeframe, + Exchange = TradingExchanges.Evm, + Ticker = ticker.ToString() + }).ToList(); + } +} diff --git a/src/Managing.Infrastructure.Web3/Managing.Infrastructure.Evm.csproj b/src/Managing.Infrastructure.Web3/Managing.Infrastructure.Evm.csproj new file mode 100644 index 0000000..a3bfd05 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Managing.Infrastructure.Evm.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + + + + + diff --git a/src/Managing.Infrastructure.Web3/Models/GeckoToken.cs b/src/Managing.Infrastructure.Web3/Models/GeckoToken.cs new file mode 100644 index 0000000..6335182 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Models/GeckoToken.cs @@ -0,0 +1,7 @@ +namespace Managing.Domain.Evm; + +public class GeckoToken +{ + public string Id { get; set; } + public string Symbol { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Web3/Models/Gmx/GmxOrder.cs b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxOrder.cs new file mode 100644 index 0000000..b64ded9 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxOrder.cs @@ -0,0 +1,19 @@ +using System.Numerics; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Models.Gmx; + +public class GmxOrder +{ + public string CollateralToken { get; internal set; } + public string IndexToken { get; internal set; } + public string CollateralDelta { get; internal set; } + public string SizeDelta { get; internal set; } + public string PurchaseToken { get; internal set; } + public string PurchaseTokenAmount { get; internal set; } + public bool IsLong { get; internal set; } + public string TriggerPrice { get; internal set; } + public bool TriggerAboveThreshold { get; internal set; } + public GmxOrderType Type { get; internal set; } + public BigInteger Index { get; internal set; } +} diff --git a/src/Managing.Infrastructure.Web3/Models/Gmx/GmxOrderIndex.cs b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxOrderIndex.cs new file mode 100644 index 0000000..d795149 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxOrderIndex.cs @@ -0,0 +1,9 @@ +namespace Managing.Infrastructure.Evm.Models.Gmx; + +public class GmxOrderIndexes +{ + public int SwapIndex { get; set; } + public int IncreaseIndex { get; set; } + public int DecreaseIndex { get; set; } + +} diff --git a/src/Managing.Infrastructure.Web3/Models/Gmx/GmxPosition.cs b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxPosition.cs new file mode 100644 index 0000000..721b8cf --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxPosition.cs @@ -0,0 +1,19 @@ +using System.Numerics; + +namespace Managing.Infrastructure.Evm.Models.Gmx; + +public class GmxPosition +{ + public string CollateralToken { get; set; } + public string IndexToken { get; set; } + public bool IsLong { get; set; } + public BigInteger SizeDelta { get; set; } + public BigInteger Collateral { get; set; } + public BigInteger AveragePrice { get; set; } + public BigInteger EntryFundingRate { get; set; } + public bool HasRealisedProfit { get; set; } + public BigInteger RealisedPnl { get; set; } + public BigInteger HasProfit { get; set; } + public BigInteger Delta { get; set; } + public BigInteger LastIncreasedTime { get; internal set; } +} diff --git a/src/Managing.Infrastructure.Web3/Models/Gmx/GmxPrices.cs b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxPrices.cs new file mode 100644 index 0000000..addb9ae --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Models/Gmx/GmxPrices.cs @@ -0,0 +1,19 @@ +namespace Managing.Infrastructure.Evm.Models.Gmx; + +public class GmxPrices +{ + public List prices { get; set; } + public string period { get; set; } + public int updatedAt { get; set; } +} + + + +public class GmxOhlc +{ + public int t { get; set; } + public double o { get; set; } + public double c { get; set; } + public double h { get; set; } + public double l { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Models/TradaoList.cs b/src/Managing.Infrastructure.Web3/Models/TradaoList.cs new file mode 100644 index 0000000..478a7a7 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Models/TradaoList.cs @@ -0,0 +1,20 @@ +namespace Managing.Infrastructure.Evm.Models; + +public class TradaoList +{ + public IList row { get; set; } + public int total { get; set; } + public int updatetime { get; set; } +} + +public class Row +{ + public string user { get; set; } + public string pnl { get; set; } + public string roi { get; set; } + public string longSize { get; set; } + public string shortSize { get; set; } + public string openLatestCollateralSum { get; set; } + public string chainId { get; set; } + +} diff --git a/src/Managing.Infrastructure.Web3/Models/TradaoUserDetails.cs b/src/Managing.Infrastructure.Web3/Models/TradaoUserDetails.cs new file mode 100644 index 0000000..02c7a49 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Models/TradaoUserDetails.cs @@ -0,0 +1,49 @@ +namespace Managing.Infrastructure.Evm.Models; + +public class TradaoUserDetails +{ + public int updatetime { get; set; } + public Summary summary { get; set; } + public IList preference { get; set; } + public IList openPositions { get; set; } +} + + +public class Summary +{ + public string pnl { get; set; } + public string roi { get; set; } + public double winRate { get; set; } + public int trades { get; set; } + public int winTrades { get; set; } + public int lossTrades { get; set; } + public string averageWin { get; set; } + public string averageLoss { get; set; } + public string openCollateralSum { get; set; } + public string fee { get; set; } + +} +public class Preference +{ + public string tokenAddress { get; set; } + public int winTrades { get; set; } + public int lossTrades { get; set; } + public string pnl { get; set; } +} + +public class OpenPositions +{ + public string indexTokenAddress { get; set; } + public string collateralTokenAddress { get; set; } + public bool isLong { get; set; } + public string position { get; set; } + public string collateral { get; set; } + public string realizedPnl { get; set; } + public string averagePrice { get; set; } + public string totalFee { get; set; } + public string entryFundingRate { get; set; } + public string borrowFee { get; set; } + public string closeFee { get; set; } + public string liqPrice { get; set; } + +} diff --git a/src/Managing.Infrastructure.Web3/Referentials/Arbitrum.cs b/src/Managing.Infrastructure.Web3/Referentials/Arbitrum.cs new file mode 100644 index 0000000..7f6586f --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Referentials/Arbitrum.cs @@ -0,0 +1,57 @@ +namespace Managing.Infrastructure.Evm.Referentials; + +public class Arbitrum +{ + public class Address + { + public const string ETH = "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"; + public const string WBTC = "0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f"; + public const string LINK = "0xf97f4df75117a78c1a5a0dbb814af92458539fb4"; + public const string UNI = "0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0"; + + public const string USDC = "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8"; + public const string USDT = "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9"; + public const string DAI = "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1"; + public const string MIM = "0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A"; + public const string FRAX = "0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F"; + + public const string Vault = "0x489ee077994B6658eAfA855C308275EAd8097C4A"; + public const string VaultPriceFeed = "0x2d68011bcA022ed0E474264145F46CC4de96a002"; + public const string Router = "0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064"; + public const string VaultReader = "0xfebB9f4CAC4cD523598fE1C5771181440143F24A"; + public const string Reader = "0xF09eD52638c22cc3f1D7F5583e3699A075e601B2"; + public const string GlpManager = "0x321F653eED006AD1C29D174e17d96351BDe22649"; + public const string RewardRouter = "0xc73d553473dC65CE56db96c58e6a091c20980fbA"; + public const string RewardReader = "0xe725Ad0ce3eCf68A7B93d8D8091E83043Ff12e9A"; + + public const string GLP = "0x4277f8f2c384827b5273592ff7cebd9f2c1ac258"; + public const string GMX = "0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a"; + public const string ES_GMX = "0xf42ae1d54fd613c9bb14810b0588faaa09a426ca"; + public const string BN_GMX = "0x35247165119B69A40edD5304969560D0ef486921"; + public const string USDG = "0x45096e7aA921f27590f8F19e457794EB09678141"; + + public const string StakedGmxTracker = "0x908C4D94D34924765f1eDc22A1DD098397c59dD4"; + public const string BonusGmxTracker = "0x4d268a7d4C16ceB5a606c173Bd974984343fea13"; + public const string FeeGmxTracker = "0xd2D1162512F927a7e282Ef43a362659E4F2a728F"; + public const string FeeGlpTracker = "0x4e971a87900b931fF39d1Aad67697F49835400b6"; + public const string StakedGlpTracker = "0x1aDDD80E6039594eE970E5872D247bf0414C8903"; + + public const string StakedGmxDistributor = "0x23208B91A98c7C1CD9FE63085BFf68311494F193"; + public const string StakedGlpDistributor = "0x60519b48ec4183a61ca2B8e37869E675FD203b34"; + + public const string GmxVester = "0x199070DDfd1CFb69173aa2F7e20906F26B363004"; + public const string GlpVester = "0xA75287d2f8b217273E7FCD7E86eF07D33972042E"; + + public const string OrderBook = "0x09f77E8A13De9a35a7231028187e9fD5DB8a2ACB"; + public const string OrderExecutor = "0x7257ac5D0a0aaC04AA7bA2AC0A6Eb742E332c3fB"; + public const string OrderBookReader = "0xa27C20A7CF0e1C68C0460706bB674f98F362Bc21"; + + public const string FastPriceFeed = "0x1a0ad27350cccd6f7f168e052100b4960efdb774"; + public const string PositionRouter = "0xb87a436B93fFE9D75c5cFA7bAcFff96430b09868"; + public const string PositionManager = "0x87a4088Bd721F83b6c2E5102e2FA47022Cb1c831"; + + public const string UniswapGmxEthPool = "0x80A9ae39310abf666A87C743d6ebBD0E8C42158E"; + + public static string Zero = "0x0000000000000000000000000000000000000000"; + } +} diff --git a/src/Managing.Infrastructure.Web3/Services/ChainService.cs b/src/Managing.Infrastructure.Web3/Services/ChainService.cs new file mode 100644 index 0000000..b73792e --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/ChainService.cs @@ -0,0 +1,70 @@ +using Managing.Common; +using Managing.Domain.Evm; + +namespace Managing.Infrastructure.Evm.Services; + +public static class ChainService +{ + //private const string RPC_ARBITRUM = "https://convincing-smart-arm.arbitrum-mainnet.discover.quiknode.pro/561ad3fa1db431a2c728c2fdb1a62e8f94acf703/"; + private const string RPC_ARBITRUM = "https://arb1.arbitrum.io/rpc"; + private const string RPC_ARBITRUM_GOERLI = "https://arb-goerli.g.alchemy.com/v2/ZMkIiKtNvgY03UtWOjho0oqkQrNt_pyc"; + private const string RPC_ETHEREUM = "https://mainnet.infura.io/v3/58f44d906ab345beadd03dd2b76348af"; + private const string RPC_ETHEREUM_GOERLI = "https://eth-goerli.g.alchemy.com/v2/xbc-eM-vxBmM9Uf1-RjjGjLp8Ng-FIc6"; + + public static Chain GetChain(string chainName) + { + if (string.IsNullOrEmpty(chainName)) + throw new Exception("Chain name is null or empty"); + + return GetChains().FirstOrDefault(c => c.Name == chainName); + } + + public static List GetChains() + { + var chains = new List() + { + GetArbitrum(), + GetEthereum(), + //GetArbitrumGoerli(), + //GetGoerli() + }; + + return chains; + } + + public static Chain GetArbitrum() + { + return new Chain() + { + Name = Constants.Chains.Arbitrum, + RpcUrl = RPC_ARBITRUM + }; + } + + public static Chain GetEthereum() + { + return new Chain() + { + Name = Constants.Chains.Ethereum, + RpcUrl = RPC_ETHEREUM + }; + } + + public static Chain GetArbitrumGoerli() + { + return new Chain() + { + Name = Constants.Chains.ArbitrumGoerli, + RpcUrl = RPC_ARBITRUM_GOERLI + }; + } + + public static Chain GetGoerli() + { + return new Chain() + { + Name = Constants.Chains.Goerli, + RpcUrl = RPC_ETHEREUM_GOERLI + }; + } +} diff --git a/src/Managing.Infrastructure.Web3/Services/Gmx/GmxHelpers.cs b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxHelpers.cs new file mode 100644 index 0000000..a193df0 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxHelpers.cs @@ -0,0 +1,139 @@ +using Managing.Domain.Trades; +using Managing.Infrastructure.Evm.Models.Gmx; +using Managing.Infrastructure.Evm.Referentials; +using Nethereum.Web3; +using System.Numerics; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Services.Gmx; + +public static class GmxHelpers +{ + + public static decimal GetQuantityForLeverage(decimal quantity, decimal? leverage) + { + return leverage.HasValue ? leverage.Value * quantity : quantity; + } + + public static (List CollateralTokens, List IndexTokens, List IsLong) GetPositionQueryData(List contractAddress) + { + var collateralToken = new List(); + var indexTokens = new List(); + var isLongs = new List(); + + foreach (var token in contractAddress) + { + collateralToken.Add(token); + indexTokens.Add(token); + isLongs.Add(true); + } + + foreach (var token in contractAddress) + { + collateralToken.Add(Arbitrum.Address.USDC); + indexTokens.Add(token); + isLongs.Add(false); + } + + return (collateralToken, indexTokens, isLongs); + } + + public static List GetIndexesRange(int lastIndex) + { + var indexes = new List(); + + var limit = 15; + var from = (lastIndex - limit) < 0 ? 0 : lastIndex - limit; + + for (int i = from; i <= lastIndex; i++) + { + indexes.Add(new BigInteger(i)); + } + + return indexes; + } + + public static BigInteger GetAcceptablePrice(decimal price, bool isLong) + { + decimal priceBasisPoints; + var basisPointDivisor = 10000m; + var allowedSlippage = 34m; + var toDecimal = 30; + + if (isLong) + { + priceBasisPoints = basisPointDivisor - allowedSlippage; + } + else + { + priceBasisPoints = basisPointDivisor + allowedSlippage; + } + var test = Web3.Convert.ToWei(price, toDecimal) * new BigInteger(priceBasisPoints); + + var priceLimit = test / Web3.Convert.ToWei(basisPointDivisor, 0); + + return priceLimit; + } + + internal static BigInteger? GetGasLimit() + { + throw new NotImplementedException(); + } + + internal static List Map(List orders, Ticker ticker) + { + return orders.ConvertAll(order => Map(order, ticker)); + + } + private static Trade Map(GmxOrder order, Ticker ticker) + { + var trade = new Trade(DateTime.UtcNow, + order.IsLong ? TradeDirection.Short : TradeDirection.Long, + TradeStatus.Requested, + GetTradeType(order.IsLong, order.TriggerAboveThreshold), + ticker, + Convert.ToDecimal(order.SizeDelta), + Convert.ToDecimal(order.TriggerPrice), + null, + "", "" + ); + + return trade; + } + + public static bool GetTriggerAboveThreshold(bool isLong, TradeType tradeType) + { + if ((isLong && tradeType == TradeType.TakeProfit) || + (!isLong && tradeType == TradeType.StopLoss)) + { + return true; + } + + return false; + } + + public static TradeType GetTradeType(bool isLong, bool isAboveThreshold) + { + if ((isLong && isAboveThreshold) || + (!isLong && !isAboveThreshold)) + { + return TradeType.TakeProfit; + } + + return TradeType.StopLoss; + } + + internal static string GeTimeframe(Timeframe timeframe) + { + return timeframe switch + { + Timeframe.FiveMinutes => "5m", + Timeframe.FifteenMinutes => "15m", + Timeframe.ThirtyMinutes => "30m", + Timeframe.OneHour => "1h", + Timeframe.FourHour => "4h", + Timeframe.OneDay => "1d", + _ => throw new NotImplementedException(), + }; + } +} diff --git a/src/Managing.Infrastructure.Web3/Services/Gmx/GmxMappers.cs b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxMappers.cs new file mode 100644 index 0000000..73d8c72 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxMappers.cs @@ -0,0 +1,177 @@ +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Domain.Trades; +using Managing.Infrastructure.Evm.Models.Gmx; +using Managing.Infrastructure.Evm.Referentials; +using Nethereum.Web3; +using System.Numerics; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Services.Gmx; + +public static class GmxMappers +{ + internal static Trade Map(GmxPosition? position, Ticker ticker) + { + if (position == null) + { + return null; + } + + var leverage = position.SizeDelta / position.Collateral; + + var trade = new Trade( + DateHelpers.GetFromUnixTimestamp((int)position.LastIncreasedTime), + position.IsLong ? TradeDirection.Long : TradeDirection.Short, + TradeStatus.Filled, + TradeType.Limit, + ticker, + Web3.Convert.FromWei(position.SizeDelta, 30), + Web3.Convert.FromWei(position.AveragePrice, 30), + (decimal)leverage, + "", + ""); + + return trade; + } + + public static List MapPositions(List positionData, + (List CollateralTokens, List IndexTokens, List IsLong) queryData) + { + var gmxPositions = new List(); + var propLength = 9; + + for (int i = 0; i < queryData.CollateralTokens.Count; i++) + { + var gmxPosition = new GmxPosition + { + CollateralToken = queryData.CollateralTokens[i], + IndexToken = queryData.IndexTokens[i], + IsLong = queryData.IsLong[i], + SizeDelta = positionData[i * propLength], + Collateral = positionData[i * propLength + 1], + AveragePrice = positionData[i * propLength + 2], + EntryFundingRate = positionData[i * propLength + 3], + //gmxPosition.CumulativeFundingRate = collateralToken.cumulativeFundingRate; + HasRealisedProfit = positionData[i * propLength + 4].Equals(1), + RealisedPnl = positionData[i * propLength + 5], + LastIncreasedTime = positionData[i * propLength + 6], + HasProfit = positionData[i * propLength + 7], + Delta = positionData[i * propLength + 8] + }; + + if (!gmxPosition.SizeDelta.IsZero) + gmxPositions.Add(gmxPosition); + } + + return gmxPositions; + } + + public static List MapIncrease( + List orderData, + List addressData, + List indexes) + { + var extractor = (List orderProperty, List address) => + { + var order = new GmxOrder + { + PurchaseToken = address[0].ToString(), + CollateralToken = address[1].ToString(), + IndexToken = address[2].ToString(), + PurchaseTokenAmount = Web3.Convert.FromWeiToBigDecimal(orderProperty[0], 18).ToString(), + SizeDelta = Web3.Convert.FromWeiToBigDecimal(orderProperty[1], 30).ToString(), + IsLong = orderProperty[2].ToString() == "1", + TriggerPrice = Web3.Convert.FromWeiToBigDecimal(orderProperty[3], 30).ToString(), + TriggerAboveThreshold = orderProperty[4].ToString() == "1", + Type = GmxOrderType.Increase + }; + + return order; + }; + + return ParseOrdersData(orderData, addressData, extractor, indexes, 5, 3); + } + + public static List MapDecrease( + List orderData, + List addressData, + List indexes) + { + var extractor = (List orderProperty, List address) => + { + var order = new GmxOrder + { + CollateralToken = address[0], + IndexToken = address[1], + CollateralDelta = orderProperty[0].ToString(), + SizeDelta = Web3.Convert.FromWeiToBigDecimal(orderProperty[1], 30).ToString(), + IsLong = orderProperty[2].ToString() == "1", + TriggerPrice = Web3.Convert.FromWeiToBigDecimal(orderProperty[3], 30).ToString(), + TriggerAboveThreshold = orderProperty[4].ToString() == "1", + Type = GmxOrderType.Decrease + }; + + return order; + }; + + return ParseOrdersData(orderData, addressData, extractor, indexes, 5, 2); + } + + public static List ParseOrdersData( + List orderData, + List addressData, + Func, List, GmxOrder> extractor, + List indexes, + int uintPropsLength, + int addressPropsLength) + { + if (orderData.Count == 0 || addressData.Count == 0) + { + return new List(); + } + + var count = orderData.Count / uintPropsLength; + + var orders = new List(); + for (int i = 0; i < count; i++) + { + var slicedAddress = addressData + .Skip(addressPropsLength * i) + .Take(addressPropsLength) + .ToList(); + + if (slicedAddress[0] == Arbitrum.Address.Zero && slicedAddress[1] == Arbitrum.Address.Zero) + { + continue; + } + + var slicedProperty = orderData + .Skip(uintPropsLength * i) + .Take(uintPropsLength * i) + .ToList(); + + var order = extractor(slicedProperty, slicedAddress); + order.Index = indexes[i]; + orders.Add(order); + } + + return orders; + } + + internal static Candle Map(GmxOhlc price, Ticker ticker, Timeframe timeframe) + { + return new Candle() + { + Date = DateHelpers.GetFromUnixTimestamp(price.t), + OpenTime = DateHelpers.GetFromUnixTimestamp(price.t).AddSeconds(-1), + Open = Convert.ToDecimal(price.o), + High = Convert.ToDecimal(price.h), + Low = Convert.ToDecimal(price.l), + Close = Convert.ToDecimal(price.c), + Exchange = TradingExchanges.Evm, + Ticker = ticker.ToString(), + Timeframe = timeframe + }; + } +} diff --git a/src/Managing.Infrastructure.Web3/Services/Gmx/GmxService.cs b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxService.cs new file mode 100644 index 0000000..318aa1a --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxService.cs @@ -0,0 +1,458 @@ +using Managing.Domain.Trades; +using Managing.Infrastructure.Evm.Models.Gmx; +using Managing.Infrastructure.Evm.Referentials; +using Managing.Tools.OrderBook; +using Managing.Tools.OrderBook.ContractDefinition; +using Managing.Tools.OrderBookReader; +using Managing.Tools.OrderBookReader.ContractDefinition; +using Managing.Tools.PositionRouter; +using Managing.Tools.PositionRouter.ContractDefinition; +using Managing.Tools.Reader; +using Managing.Tools.Reader.ContractDefinition; +using Managing.Tools.Router; +using Managing.Tools.Router.ContractDefinition; +using Nethereum.Contracts.Standards.ERC20; +using Nethereum.Contracts.Standards.ERC20.ContractDefinition; +using Nethereum.Util; +using Nethereum.Web3; +using System.Numerics; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Services.Gmx; + +public static class GmxService +{ + private const decimal _orderFeesExecution = 0.0003m; + private const decimal _positionUpdateFees = 0.0001m; + + public async static Task InitAccountForTrading(Web3 web3, string publicAddress, List tickers) + { + var router = new RouterService(web3, Arbitrum.Address.Router); + if (!await IsPluginAdded(web3, publicAddress, Arbitrum.Address.PositionRouter)) + { + var routerApproval = await router + .ApprovePluginRequestAndWaitForReceiptAsync(Arbitrum.Address.PositionRouter); + } + + + if (!await IsPluginAdded(web3, publicAddress, Arbitrum.Address.OrderBook)) + { + var routerApproval = await router + .ApprovePluginRequestAsync(Arbitrum.Address.OrderBook); + } + + foreach (var ticker in tickers) + { + var conntractAddress = TokenService.GetContractAddress(ticker); + await ApproveToken(web3, publicAddress, conntractAddress); + } + } + + public async static Task IsPluginAdded(Web3 web3, string publicAddress, string pluginAddress) + { + var router = new RouterService(web3, Arbitrum.Address.Router); + var function = new ApprovedPluginsFunction + { + ReturnValue1 = publicAddress, + ReturnValue2 = pluginAddress + }; + + var isAdded = await router.ApprovedPluginsQueryAsync(function); + return isAdded; + } + + public async static Task ApproveToken(Web3 web3, string publicAddress, string contractAddress) + { + var input = new Nethereum.Contracts.Standards.ERC20.ContractDefinition.ApproveFunction + { + Spender = publicAddress + }; + + var contract = new ERC20ContractService(web3.Eth, contractAddress); + var approval = await contract.ApproveRequestAsync(input); + } + + public static async Task ApproveOrder(Web3 web3, Ticker ticker, string publicAddress, decimal amount) + { + var contractAddress = TokenService.GetContractAddress(ticker); + var contract = new ERC20ContractService(web3.Eth, contractAddress); + + var allowanceQuery = new AllowanceFunction + { + Owner = publicAddress, + Spender = Arbitrum.Address.Router + }; + + var allowance = await contract.AllowanceQueryAsync(allowanceQuery); + + if (allowance.IsZero) return false; + + var approveQuery = new Nethereum.Contracts.Standards.ERC20.ContractDefinition.ApproveFunction + { + FromAddress = publicAddress, + Spender = Arbitrum.Address.Router, + Value = Web3.Convert.ToWei(amount) + }; + + var approval = await contract.ApproveRequestAsync(approveQuery); + return true; + } + + public async static Task IncreasePosition(Web3 web3, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage) + { + var quantityLeveraged = GmxHelpers.GetQuantityForLeverage(quantity, leverage); + var orderBook = new OrderBookService(web3, Arbitrum.Address.OrderBook); + var contractAddress = TokenService.GetContractAddress(ticker); + var isLong = direction == TradeDirection.Long; + var function = new CreateIncreaseOrderFunction(); + + // Forcing path to use USDC to pay the trade + function.Path = new List { Arbitrum.Address.USDC }; + function.AmountIn = Web3.Convert.ToWei(quantity * price, 6); // Price in $ to pay the long/short. Ex 11.42$ + function.IndexToken = contractAddress; // Token to long/short + function.MinOut = new BigInteger(0); + // Size of the position with the leveraged quantity + // Ex : Long 11$ x3. SizeDelta = 33$ + function.SizeDelta = Web3.Convert.ToWei(quantityLeveraged * price, UnitConversion.EthUnit.Tether); + function.CollateralToken = Arbitrum.Address.USDC; // USDC + function.IsLong = isLong; + function.TriggerPrice = Web3.Convert.ToWei(price, 30); // Price of the order execution + function.TriggerAboveThreshold = false; + function.ExecutionFee = Web3.Convert.ToWei(_orderFeesExecution); // Fee required to execute tx + function.ShouldWrap = false; + + // Specify the tx opts + function.AmountToSend = Web3.Convert.ToWei(_orderFeesExecution); + function.FromAddress = publicAddress; + //function.MaxFeePerGas = await orderBook.ContractHandler.EstimateGasAsync(function); + function.GasPrice = GetGasPrice(); + + // Approving Router to transfer ERC20 token + var approval = await ApproveOrder(web3, Ticker.USDC, publicAddress, _orderFeesExecution); + + if (!approval) return null; + + var receipt = await orderBook + .CreateIncreaseOrderRequestAndWaitForReceiptAsync(function); + + var trade = new Trade(DateTime.UtcNow, + direction, + TradeStatus.Requested, + TradeType.Limit, + ticker, + quantity, + price, + leverage, + receipt.TransactionHash, + ""); + + return trade; + } + + public async static Task DecreasePosition(Web3 web3, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage) + { + var trade = new Trade(DateTime.UtcNow, + direction, + TradeStatus.Cancelled, + TradeType.Market, + ticker, + quantity, + price, + leverage, + "", + ""); + + // Check if there is quantity in position + if (await QuantityInPosition(web3, publicAddress, ticker) == 0) return trade; + + var quantityLeveraged = GmxHelpers.GetQuantityForLeverage(quantity, leverage); + var positionRouter = new PositionRouterService(web3, Arbitrum.Address.PositionRouter); + var contractAddress = TokenService.GetContractAddress(ticker); + var isLong = direction == TradeDirection.Long; + var function = new CreateDecreasePositionFunction(); + + // Forcing path to use contract address to widthdraw funds + // The address for closing a short, should be USDC + //function.Path = new List { contractAddress }; + function.Path = new List { Arbitrum.Address.USDC }; + // the index token of the position + function.IndexToken = contractAddress; // Token to long/short + // the amount of collateral in USD value to withdraw + function.CollateralDelta = new BigInteger(0); // Price in $ to pay the long/short. Ex 11.42$ + //function.CollateralDelta = Web3.Convert.ToWei(quantity * price, 6); // Price in $ to pay the long/short. Ex 11.42$ + // the USD value of the change in position size + function.SizeDelta = Web3.Convert.ToWei(quantity, UnitConversion.EthUnit.Tether); + function.IsLong = isLong; + // the address to receive the withdrawn tokens + function.Receiver = publicAddress; + // the USD value of the min (for longs) or max (for shorts) index price acceptable when executing the request + function.AcceptablePrice = GmxHelpers.GetAcceptablePrice(price, isLong); + // the min output token amount + function.MinOut = new BigInteger(0); + function.ExecutionFee = Web3.Convert.ToWei(_positionUpdateFees); // Fee required to execute tx + function.WithdrawETH = false; + function.CallbackTarget = Arbitrum.Address.Zero; + + // Specify the tx opts + function.AmountToSend = Web3.Convert.ToWei(_positionUpdateFees); + function.FromAddress = publicAddress; + function.MaxFeePerGas = await positionRouter.ContractHandler.EstimateGasAsync(function); + function.GasPrice = GetGasPrice(); + + var approval = await ApproveOrder(web3, ticker, publicAddress, _positionUpdateFees); + + if (!approval) return null; + + var receipt = await positionRouter + .CreateDecreasePositionRequestAndWaitForReceiptAsync(function); + + trade.SetExchangeOrderId(receipt.TransactionHash); + trade.SetStatus(receipt.Status.Value.IsOne ? TradeStatus.Requested : TradeStatus.Cancelled); + + return trade; + } + + public static async Task DecreaseOrder(Web3 web3, TradeType tradeType, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage) + { + var trade = new Trade(DateTime.UtcNow, + direction, + TradeStatus.Cancelled, + tradeType, + ticker, + quantity, + price, + leverage, + "", + ""); + + // Check if there is quantity in position + var currentPosition = await GetGmxPosition(web3, publicAddress, ticker); + + if (currentPosition == null || currentPosition?.SizeDelta == 0) return trade; + + var quantityLeveraged = GmxHelpers.GetQuantityForLeverage(quantity, leverage); + var orderbook = new OrderBookService(web3, Arbitrum.Address.OrderBook); + var contractAddress = TokenService.GetContractAddress(ticker); + var isLong = direction != TradeDirection.Long; + var function = new CreateDecreaseOrderFunction(); + + // the index token of the position + function.IndexToken = contractAddress; // Token to long/short + // the USD value of the change in position size + function.SizeDelta = currentPosition.SizeDelta; + function.CollateralToken = Arbitrum.Address.USDC; + // the amount of collateral in USD value to withdraw + function.CollateralDelta = new BigInteger(0); // Price in $ to pay the long/short. Ex 11.42$ + //function.CollateralDelta = Web3.Convert.ToWei(quantity * price, 6); // Price in $ to pay the long/short. Ex 11.42$ + function.IsLong = isLong; + // the USD value of the min (for longs) or max (for shorts) index price acceptable when executing the request + function.TriggerPrice = GmxHelpers.GetAcceptablePrice(price, isLong); + function.TriggerAboveThreshold = GmxHelpers.GetTriggerAboveThreshold(isLong, tradeType); + + // Specify the tx opts + function.AmountToSend = Web3.Convert.ToWei(_orderFeesExecution); + function.FromAddress = publicAddress; + function.MaxFeePerGas = await orderbook.ContractHandler.EstimateGasAsync(function); + function.GasPrice = GetGasPrice(); + + var approval = await ApproveOrder(web3, ticker, publicAddress, _positionUpdateFees); + + if (!approval) return null; + + var receipt = await orderbook + .CreateDecreaseOrderRequestAndWaitForReceiptAsync(function); + + trade.SetExchangeOrderId(receipt.TransactionHash); + trade.SetStatus(TradeStatus.Requested); + return trade; + } + + public async static Task CancelOrders(Web3 web3, string publicAddress, Ticker ticker) + { + var orderBook = new OrderBookService(web3, Arbitrum.Address.OrderBook); + var orders = await GetOrders(web3, publicAddress, ticker); + + if (!orders.Any()) return true; + + var function = new CancelMultipleFunction(); + var increaseOrderIndexes = orders.Where(i => i.Type == GmxOrderType.Increase); + function.IncreaseOrderIndexes = increaseOrderIndexes.Select(o => o.Index).ToList(); + + var decreaseOrderIndexes = orders.Where(i => i.Type == GmxOrderType.Decrease); + function.DecreaseOrderIndexes = decreaseOrderIndexes.Select(o => o.Index).ToList(); + + function.SwapOrderIndexes = new List(); + + try + { + if (function.DecreaseOrderIndexes.Any() || function.IncreaseOrderIndexes.Any()) + { + function.MaxFeePerGas = await orderBook.ContractHandler.EstimateGasAsync(function); + function.GasPrice = GetGasPrice(); + + var cancellation = await orderBook.CancelMultipleRequestAndWaitForReceiptAsync(function); + } + } + catch (Exception ex) + { + return false; + } + + return true; + } + + private static BigInteger GetGasPrice() + { + return Web3.Convert.ToWei(0.1, UnitConversion.EthUnit.Gwei); + } + + public static async Task> GetOrders(Web3 web3, string publicAddress, Ticker ticker) + { + var lastIndexes = await GetLastIndex(web3, publicAddress); + var orders = new List(); + var orderBookReader = new OrderBookReaderService(web3, Arbitrum.Address.OrderBookReader); + + var increaseOrders = await GetIncreaseOrders(orderBookReader, publicAddress, lastIndexes.IncreaseIndex); + var decreaseOrders = await GetDecreaseOrders(orderBookReader, publicAddress, lastIndexes.DecreaseIndex); + + orders.AddRange(increaseOrders); + orders.AddRange(decreaseOrders); + var contractAddress = TokenService.GetContractAddress(ticker); + var ordersFiltered = orders.Where(o => string.Equals(o.IndexToken, contractAddress, StringComparison.CurrentCultureIgnoreCase)); + + return ordersFiltered.ToList(); + } + + private static async Task> GetIncreaseOrders(OrderBookReaderService orderBookReader, string publicAddress, int lastIndex) + { + var increaseIndex = GmxHelpers.GetIndexesRange(lastIndex); + var increaseOrdersFunction = new GetIncreaseOrdersFunction + { + OrderBookAddress = Arbitrum.Address.OrderBook, + Account = publicAddress, + Indices = increaseIndex, + }; + + var increaseOrders = await orderBookReader.GetIncreaseOrdersQueryAsync(increaseOrdersFunction); + + return GmxMappers.MapIncrease(increaseOrders.ReturnValue1, increaseOrders.ReturnValue2, increaseIndex); + } + + private static async Task> GetDecreaseOrders(OrderBookReaderService orderBookReader, string publicAddress, int lastIndex) + { + var increaseIndex = GmxHelpers.GetIndexesRange(lastIndex); + var increaseOrdersFunction = new GetDecreaseOrdersFunction + { + OrderBookAddress = Arbitrum.Address.OrderBook, + Account = publicAddress, + Indices = increaseIndex, + }; + + var increaseOrders = await orderBookReader.GetDecreaseOrdersQueryAsync(increaseOrdersFunction); + + return GmxMappers.MapDecrease(increaseOrders.ReturnValue1, increaseOrders.ReturnValue2, increaseIndex); + } + + public static async Task GetLastIndex(Web3 web3, string publicAddress) + { + var orderBook = new OrderBookService(web3, Arbitrum.Address.OrderBook); + var increaseFunction = new IncreaseOrdersIndexFunction + { + ReturnValue1 = publicAddress + }; + var decreaseFunction = new DecreaseOrdersIndexFunction + { + ReturnValue1 = publicAddress + }; + var swapFunction = new SwapOrdersIndexFunction + { + ReturnValue1 = publicAddress + }; + + var increaseIndex = await orderBook.IncreaseOrdersIndexQueryAsync(increaseFunction); + var decreaseIndex = await orderBook.DecreaseOrdersIndexQueryAsync(decreaseFunction); + var swapIndex = await orderBook.SwapOrdersIndexQueryAsync(swapFunction); + + var indexes = new GmxOrderIndexes + { + SwapIndex = (int)swapIndex > 0 ? (int)swapIndex - 1 : (int)swapIndex, + IncreaseIndex = (int)increaseIndex > 0 ? (int)increaseIndex - 1 : (int)increaseIndex, + DecreaseIndex = (int)decreaseIndex > 0 ? (int)decreaseIndex - 1 : (int)decreaseIndex + }; + + return indexes; + } + + public static async Task GetTrade(Web3 web3, string publicAddress, Ticker ticker) + { + var position = await GetGmxPosition(web3, publicAddress, ticker); + return GmxMappers.Map(position, ticker); + } + + public static async Task GetGmxPosition(Web3 web3, string publicAddress, Ticker ticker) + { + var reader = new ReaderService(web3, Arbitrum.Address.Reader); + var contractAddress = TokenService.GetContractAddress(ticker); + var queryData = GmxHelpers.GetPositionQueryData(new List { contractAddress }); + + var function = new GetPositionsFunction + { + Vault = Arbitrum.Address.Vault, + Account = publicAddress, + CollateralTokens = queryData.CollateralTokens, + IndexTokens = queryData.IndexTokens, + IsLong = queryData.IsLong + }; + + var result = await reader.GetPositionsQueryAsync(function); + + var positions = GmxMappers.MapPositions(result, queryData); + var position = positions.FirstOrDefault(p => p.IndexToken == contractAddress); + return position; + } + + public static async Task QuantityInPosition(Web3 web3, string key, Ticker ticker) + { + var position = await GetTrade(web3, key, ticker); + return position?.Quantity ?? 0m; + } + + public static async Task GetFee(Web3 web3, decimal ethPrice) + { + var positionRouter = new PositionRouterService(web3, Arbitrum.Address.PositionRouter); + var contractAddress = TokenService.GetContractAddress(Ticker.BTC); + var function = new CreateDecreasePositionFunction(); + + function.Path = new List { contractAddress }; + function.IndexToken = contractAddress; // Token to long/short + function.CollateralDelta = new BigInteger(0); // Price in $ to pay the long/short. Ex 11.42$ + function.SizeDelta = Web3.Convert.ToWei(100, UnitConversion.EthUnit.Tether); + function.IsLong = true; + function.Receiver = Arbitrum.Address.Zero; + function.AcceptablePrice = GmxHelpers.GetAcceptablePrice(100, true); + function.MinOut = new BigInteger(0); + function.ExecutionFee = Web3.Convert.ToWei(_positionUpdateFees); // Fee required to execute tx + function.WithdrawETH = false; + function.CallbackTarget = Arbitrum.Address.Zero; + + function.AmountToSend = Web3.Convert.ToWei(_positionUpdateFees); + function.FromAddress = Arbitrum.Address.Zero; + + var totalCost = 0m; + + try + { + var gasCost = await positionRouter.ContractHandler.EstimateGasAsync(function); + var gasPrice = GetGasPrice(); + var gas = gasPrice * gasCost; + totalCost = ethPrice * Web3.Convert.FromWei(gas, 18); + return totalCost; + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return totalCost; + } +} diff --git a/src/Managing.Infrastructure.Web3/Services/NftService.cs b/src/Managing.Infrastructure.Web3/Services/NftService.cs new file mode 100644 index 0000000..9e1fa19 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/NftService.cs @@ -0,0 +1,45 @@ +using Nethereum.Contracts; +using Nethereum.Contracts.Standards.ERC721.ContractDefinition; +using Nethereum.Web3; + +namespace Managing.Infrastructure.Evm.Services; + +public static class NftService +{ + public static async Task>> GetNftEvent(Web3 web3, string owner, string contract) + { + try + { + // Retrieve transfer event of the contract + var transferEvent = web3.Eth.GetEvent(contract); + + // Create IN & OUT filter to filter the return transfers changes + var transferFilterIn = transferEvent.CreateFilterInput(null, owner); + var transferFilterOut = transferEvent.CreateFilterInput(owner); + + // Retrieve changes based on filter + var transferInLogs = await transferEvent.GetAllChangesAsync(transferFilterIn); + var transferOutLogs = await transferEvent.GetAllChangesAsync(transferFilterOut); + + var list = new List>(); + + // For each transfer IN, we add the event into the list + foreach (var ins in transferInLogs) + { + list.Add(ins); + } + + // Remove all transfer OUT of the list because the use might already send the token + foreach (var ins in transferOutLogs) + { + list.Remove(ins); + } + return list; + } + catch (Exception e) + { + Console.WriteLine(e.Message); + return null; + } + } +} diff --git a/src/Managing.Infrastructure.Web3/Services/SubgraphService.cs b/src/Managing.Infrastructure.Web3/Services/SubgraphService.cs new file mode 100644 index 0000000..7ac3fd0 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/SubgraphService.cs @@ -0,0 +1,45 @@ +using GraphQL.Client.Http; +using GraphQL.Client.Serializer.SystemTextJson; +using Managing.Domain.Evm; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Services; + +public static class SubgraphService +{ + private const string SUBGRAPH_UNISWAP_V2 = "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2"; + private const string SUBGRAPH_CHAINLINK = "https://api.thegraph.com/subgraphs/name/openpredict/chainlink-prices-subgraph"; + private const string SUBGRAPH_CHAINLINK_GMX = "https://api.thegraph.com/subgraphs/name/deividask/chainlink"; + private const string SUBGRAPH_GBC = "https://api.thegraph.com/subgraphs/name/nissoh/gmx-arbitrum"; + + public static GraphQLHttpClient GetSubgraphClient(SubgraphProvider subgraphProvider) + { + var url = GetSubgraph(subgraphProvider).Url; + var graphQLOptions = new GraphQLHttpClientOptions + { + EndPoint = new Uri(url) + }; + return new GraphQLHttpClient(graphQLOptions, new SystemTextJsonSerializer()); + } + + private static Subgraph GetSubgraph(SubgraphProvider subgraphProvider) + { + return new Subgraph() + { + SubgraphProvider = subgraphProvider, + Url = GetSubgraphUrl(subgraphProvider) + }; + } + + private static string GetSubgraphUrl(SubgraphProvider subgraphProvider) + { + return subgraphProvider switch + { + SubgraphProvider.UniswapV2 => SUBGRAPH_UNISWAP_V2, + SubgraphProvider.ChainlinkPrice => SUBGRAPH_CHAINLINK, + SubgraphProvider.ChainlinkGmx => SUBGRAPH_CHAINLINK_GMX, + SubgraphProvider.Gbc => SUBGRAPH_GBC, + _ => throw new Exception("No url for subgraphprovider") + }; + } +} diff --git a/src/Managing.Infrastructure.Web3/Services/TokenService.cs b/src/Managing.Infrastructure.Web3/Services/TokenService.cs new file mode 100644 index 0000000..6a29d7f --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/TokenService.cs @@ -0,0 +1,65 @@ +using Managing.Domain.Evm; +using Managing.Infrastructure.Evm.Referentials; +using Nethereum.Contracts.Standards.ERC20.TokenList; +using Newtonsoft.Json; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Services; + +public static class TokenService +{ + public static string TokensJSON = @"{""name"":""Defiprime"",""logoURI"":""https://defiprime.com/images/defiprime-logo-hires2.png"",""keywords"":[""Defi"",""defiprime"",""curated""],""tags"":{""defi"":{""name"":""DeFi Tokens"",""description"":""Tokens associated with the products listed at defiprime.com""}},""timestamp"":""2022-11-29T00:00:00+00:00"",""tokens"":[{""chainId"":1,""address"":""0xa1d65E8fB6e87b60FECCBc582F7f97804B725521"",""symbol"":""DXD"",""name"":""DXdao"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F"",""symbol"":""SNX"",""name"":""Synthetix"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e"",""symbol"":""YFI"",""name"":""Yearn.finance"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x27054b13b1B798B345b591a4d22e6562d47eA75a"",""symbol"":""AST"",""name"":""AirSwap"",""decimals"":4,""tags"":[""defi""]},{""chainId"":1,""address"":""0xba100000625a3754423978a60c9317c58a424e3D"",""symbol"":""BAL"",""name"":""Balancer"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C"",""symbol"":""BNT"",""name"":""Bancor"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xd533a949740bb3306d119cc777fa900ba034cd52"",""symbol"":""CRV"",""name"":""Curve"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xcc80c051057b774cd75067dc48f8987c4eb97a5e"",""symbol"":""NEC"",""name"":""DeversiFi"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xB705268213D593B8FD88d3FDEFF93AFF5CbDcfAE"",""symbol"":""IDEX"",""name"":""IDEX"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xdeFA4e8a7bcBA345F687a2f1456F5Edd9CE97202"",""symbol"":""KNC"",""name"":""KyberSwap"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD"",""symbol"":""LRC"",""name"":""Loopring Exchange"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xe41d2489571d322189246dafa5ebde1f4699f498"",""symbol"":""ZRX"",""name"":""Matcha"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x56d811088235F11C8920698a204A5010a788f4b3"",""symbol"":""BZRX"",""name"":""bZx"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x514910771AF9Ca656af840dff83E8264EcF986CA"",""symbol"":""LINK"",""name"":""Chainlink"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xec67005c4e498ec7f55e092bd1d35cbc47c91892"",""symbol"":""MLN"",""name"":""Melon Protocol"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x408e41876cccdc0f92210600ef50372656052a38"",""symbol"":""REN"",""name"":""Ren"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828"",""symbol"":""UMA"",""name"":""Uma"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x0d438f3b5175bebc262bf23753c1e53d03432bde"",""symbol"":""wNXM"",""name"":""Nexus Mutual"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x107c4504cd79c5d2696ea0030a8dd4e92601b82e"",""symbol"":""BLT"",""name"":""Bloom"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x41e5560054824eA6B0732E656E3Ad64E20e94E45"",""symbol"":""CVC"",""name"":""Civic"",""decimals"":8,""tags"":[""defi""]},{""chainId"":1,""address"":""0xebbdf302c940c6bfd49c6b165f457fdb324649bc"",""symbol"":""HYDRO"",""name"":""Hydro"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x4cc19356f2d37338b9802aa8e8fc58b0373296e7"",""symbol"":""KEY"",""name"":""SelfKey"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x80fB784B7eD66730e8b1DBd9820aFD29931aab03"",""symbol"":""LEND"",""name"":""Aave"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xc00e94cb662c3520282e6f5717214004a7f26888"",""symbol"":""COMP"",""name"":""Compound"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2"",""symbol"":""MKR"",""name"":""MakerDAO"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x4e352cf164e64adcbad318c3a1e222e9eba4ce42"",""symbol"":""MCB"",""name"":""MCDEX"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x0abdace70d3790235af448c88547603b945604ea"",""symbol"":""DNT"",""name"":""District0x"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xfca59cd816ab1ead66534d82bc21e7515ce441cf"",""symbol"":""RARI"",""name"":""Rarible"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"",""symbol"":""MATIC"",""name"":""Matic"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xd26114cd6EE289AccF82350c8d8487fedB8A0C07"",""symbol"":""OMG"",""name"":""OmiseGO"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x0Ae055097C6d159879521C384F1D2123D1f195e6"",""symbol"":""STAKE"",""name"":""xDai Stable Chain"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x6810e776880c02933d47db1b9fc05908e5386b96"",""symbol"":""GNO"",""name"":""Gnosis"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xd46ba6d942050d489dbd938a2c909a5d5039a161"",""symbol"":""AMPL"",""name"":""Ampleforth"",""decimals"":9,""tags"":[""defi""]},{""chainId"":1,""address"":""0x6b175474e89094c44da98b954eedeac495271d0f"",""symbol"":""DAI"",""name"":""DAI"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x4f3afec4e5a3f2a6a1a411def7d7dfe50ee057bf"",""symbol"":""DGX"",""name"":""Digix"",""decimals"":9,""tags"":[""defi""]},{""chainId"":1,""address"":""0x056fd409e1d7a124bd7017459dfea2f387b6d5cd"",""symbol"":""GUSD"",""name"":""Gemini Dollar"",""decimals"":2,""tags"":[""defi""]},{""chainId"":1,""address"":""0x8e870d67f660d95d5be530380d0ec0bd388289e1"",""symbol"":""USDP"",""name"":""Pax Dollar"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"",""symbol"":""USDC"",""name"":""USD Coin"",""decimals"":6,""tags"":[""defi""]},{""chainId"":1,""address"":""0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"",""symbol"":""WBTC"",""name"":""WBTC"",""decimals"":8,""tags"":[""defi""]},{""chainId"":1,""address"":""0xdf574c24545e5ffecb9a659c229253d4111d87e1"",""symbol"":""HUSD"",""name"":""HUSD"",""decimals"":8,""tags"":[""defi""]},{""chainId"":1,""address"":""0x5BC25f649fc4e26069dDF4cF4010F9f706c23831"",""symbol"":""DUSD"",""name"":""DefiDollar"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x6b3595068778dd592e39a122f4f5a5cf09c90fe2"",""symbol"":""SUSHI"",""name"":""SushiSwap"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"",""symbol"":""UNI"",""name"":""Uniswap"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x543ff227f64aa17ea132bf9886cab5db55dcaddf"",""symbol"":""GEN"",""name"":""DAOstack"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xa0246c9032bc3a600820415ae600c6388619a14d"",""symbol"":""FARM"",""name"":""Harvest"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9"",""symbol"":""AAVE"",""name"":""AAVE"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x43dfc4159d86f3a37a5a4b3d4580b888ad7d4ddd"",""symbol"":""DODO"",""name"":""DODO"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xa117000000f279D81A1D3cc75430fAA017FA5A2e"",""symbol"":""ANT"",""name"":""Aragon"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x4688a8b1f292fdab17e9a90c8bc379dc1dbd8713"",""symbol"":""COVER"",""name"":""COVER"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x36f3fd68e7325a35eb768f1aedaae9ea0689d723"",""symbol"":""ESD"",""name"":""Empty Set Dollar"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xeF9Cd7882c067686691B6fF49e650b43AFBBCC6B"",""symbol"":""FNX"",""name"":""FinNexus"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xca1207647ff814039530d7d35df0e1dd2e91fa84"",""symbol"":""DHT"",""name"":""dHEDGE"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xfFffFffF2ba8F66D4e51811C5190992176930278"",""symbol"":""COMBO"",""name"":""Furucombo"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x853d955aCEf822Db058eb8505911ED77F175b99e"",""symbol"":""FRAX"",""name"":""Frax"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0"",""symbol"":""FXS"",""name"":""Frax Share"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x86772b1409b61c639EaAc9Ba0AcfBb6E238e5F83"",""symbol"":""NDX"",""name"":""Indexed"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b"",""symbol"":""DPI"",""name"":""DefiPulse Index"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xad32a8e6220741182940c5abf610bde99e737b2d"",""symbol"":""DOUGH"",""name"":""PieDAO"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x0000000000095413afc295d19edeb1ad7b71c952"",""symbol"":""LON"",""name"":""Tokenlon"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x888888888889c00c67689029d7856aac1065ec11"",""symbol"":""OPIUM"",""name"":""OPIUM"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x09a3ecafa817268f77be1283176b946c4ff2e608"",""symbol"":""MIR"",""name"":""Mirror Protocol"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x8888801af4d980682e47f1a9036e589479e835c5"",""symbol"":""MPH"",""name"":""88mph"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x4c19596f5aaff459fa38b0f7ed92f11ae6543784"",""symbol"":""TRU"",""name"":""TrueFi"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x87d73e916d7057945c9bcd8cdd94e42a6f47f776"",""symbol"":""NFTX"",""name"":""NFTX"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x429881672b9ae42b8eba0e26cd9c73711b891ca5"",""symbol"":""PICKLE"",""name"":""PICKLE"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x875773784af8135ea0ef43b5a374aad105c5d39e"",""symbol"":""IDLE"",""name"":""IDLE"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xd291e7a03283640fdc51b121ac401383a46cc623"",""symbol"":""RGT"",""name"":""RGT"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xa1faa113cbe53436df28ff0aee54275c13b40975"",""symbol"":""ALPHA"",""name"":""ALPHA"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x584bc13c7d411c00c01a62e8019472de68768430"",""symbol"":""HEGIC"",""name"":""HEGIC"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x49e833337ece7afe375e44f4e3e8481029218e5c"",""symbol"":""VALUE"",""name"":""VALUE"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xb6ca7399b4f9ca56fc27cbff44f4d2e4eef1fc81"",""symbol"":""MUSE"",""name"":""MUSE"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x92e187a03b6cd19cb6af293ba17f2745fd2357d5"",""symbol"":""DUCK"",""name"":""DUCK"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x111111111117dc0aa78b770fa6a738034120c302"",""symbol"":""1INCH"",""name"":""1INCH"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x1b40183efb4dd766f11bda7a7c3ad8982e998421"",""symbol"":""VSP"",""name"":""VSP"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7"",""symbol"":""AKRO"",""name"":""Akropolis"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x03ab458634910aad20ef5f1c8ee96f1d6ac54919"",""symbol"":""RAI"",""name"":""RAI"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x77fba179c79de5b7653f68b5039af940ada60ce0"",""symbol"":""FORTH"",""name"":""FORTH"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xab37e1358b639fd877f015027bb62d3ddaa7557e"",""symbol"":""LIEN"",""name"":""LIEN"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x6c28aef8977c9b773996d0e8376d2ee379446f2f"",""symbol"":""QUICK"",""name"":""QUICK"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xde30da39c46104798bb5aa3fe8b9e0e1f348163f"",""symbol"":""GTC"",""name"":""GTC"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xdbdb4d16eda451d0503b854cf79d55697f90c8df"",""symbol"":""ALCX"",""name"":""ALCX"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x6dea81c8171d0ba574754ef6f8b412f2ed88c54d"",""symbol"":""LQTY"",""name"":""LQTY"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x5a98fcbea516cf06857215779fd812ca3bef1b32"",""symbol"":""LDO"",""name"":""LDO"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x6f40d4a6237c257fff2db00fa0510deeecd303eb"",""symbol"":""INST"",""name"":""INST"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x72f020f8f3e8fd9382705723cd26380f8d0c66bb"",""symbol"":""PLOT"",""name"":""PLOT"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x8f8221afbb33998d8584a2b05749ba73c37a938a"",""symbol"":""REQ"",""name"":""REQ"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x321c2fe4446c7c963dc41dd58879af648838f98d"",""symbol"":""CTX"",""name"":""CTX"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x16c52ceece2ed57dad87319d91b5e3637d50afa4"",""symbol"":""TCAP"",""name"":""TCAP"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x92d6c1e31e14520e676a687f0a93788b716beff5"",""symbol"":""DYDX"",""name"":""DYDX"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x6123b0049f904d730db3c36a31167d9d4121fa6b"",""symbol"":""RBN"",""name"":""RBN"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x73968b9a57c6e53d41345fd57a6e6ae27d6cdb2f"",""symbol"":""SDT"",""name"":""SDT"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xcafe001067cdef266afb7eb5a286dcfd277f3de5"",""symbol"":""PSP"",""name"":""PSP"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x64aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5"",""symbol"":""OHM"",""name"":""OHM"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x72b886d09c117654ab7da13a14d603001de0b777"",""symbol"":""XDEFI"",""name"":""XDEFI"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0x3ec8798b81485a254928b70cda1cf0a2bb0b74d7"",""symbol"":""GRO"",""name"":""GRO"",""decimals"":18,""tags"":[""defi""]},{""chainId"":1,""address"":""0xf4d2888d29d722226fafa5d9b24f9164c092421e"",""symbol"":""LOOKS"",""name"":""LOOKS"",""decimals"":18,""tags"":[""defi""]},{""chainId"":42161,""address"":""0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a"",""symbol"":""GMX"",""name"":""GMX"",""tags"":[""defi""]},{""chainId"":1,""address"":""0x7778360f035c589fce2f4ea5786cbd8b36e5396b"",""symbol"":""OOE"",""name"":""OOE"",""tags"":[""defi""]},{""chainId"":1,""address"":""0xd9fcd98c322942075a5c3860693e9f4f03aae07b"",""symbol"":""EUL"",""name"":""EUL"",""tags"":[""defi""]},{""chainId"":1,""address"":""0x8290333cef9e6d528dd5618fb97a76f268f3edd4"",""symbol"":""ANKR"",""name"":""ANKR"",""tags"":[""defi""]},{""chainId"":10,""address"":""0x920cf626a271321c151d027030d5d08af699456b"",""symbol"":""KWENTA"",""name"":""KWENTA"",""tags"":[""defi""]}],""version"":{""major"":1,""minor"":45,""patch"":0}}"; + public static string GeckoJson = @"[{'Id':'blockstack','Symbol':'stx'},{'Id':'bitcoin','Symbol':'btc'},{'Id':'liquidity-dividends-protocol','Symbol':'LID'},{'Id':'uma','Symbol':'uma'},{'Id':'uptrennd','Symbol':'1up'},{'Id':'math','Symbol':'math'},{'Id':'dos-network','Symbol':'dos'},{'Id':'xdai-stake','Symbol':'stake'},{'Id':'tellor','Symbol':'trb'},{'Id':'yearn-finance','Symbol':'yfi'},{'Id':'streamr-datacoin','Symbol':'data'},{'Id':'wrapped-nxm','Symbol':'wnxm'},{'Id':'basic-attention-token','Symbol':'bat'},{'Id':'the-abyss','Symbol':'abyss'},{'Id':'decentraland','Symbol':'mana'},{'Id':'xio','Symbol':'xio'},{'Id':'grid','Symbol':'grid'},{'Id':'howdoo','Symbol':'udoo'},{'Id':'curio','Symbol':'cur'},{'Id':'tendies','Symbol':'tend'},{'Id':'numeraire','Symbol':'nmr'},{'Id':'owl','Symbol':'owl'},{'Id':'parachute','Symbol':'par'},{'Id':'reserve','Symbol':'rsv'},{'Id':'bancor','Symbol':'bnt'},{'Id':'sapien','Symbol':'spn'},{'Id':'wrapped-bitcoin','Symbol':'wbtc'},{'Id':'raiden-network','Symbol':'rdn'},{'Id':'toshify-finance','Symbol':'YFT'},{'Id':'idextools','Symbol':'dext'},{'Id':'airswap','Symbol':'ast'},{'Id':'yflink','Symbol':'yfl'},{'Id':'blitzpredict','Symbol':'xbp'},{'Id':'hex','Symbol':'hex'},{'Id':'cream-2','Symbol':'cream'},{'Id':'simple-token','Symbol':'ost'},{'Id':'bilira','Symbol':'tryb'},{'Id':'viberate','Symbol':'vib'},{'Id':'global-digital-content','Symbol':'gdc'},{'Id':'usd-bancor','Symbol':'usdb'},{'Id':'dark-energy-crystals','Symbol':'dec'},{'Id':'q-dao-governance-token-v1-0','Symbol':'qdao'},{'Id':'blockv','Symbol':'vee'},{'Id':'aidcoin','Symbol':'aid'},{'Id':'tokenbox','Symbol':'tbx'},{'Id':'peerex-network','Symbol':'PERX'},{'Id':'rivetz','Symbol':'rvt'},{'Id':'republic-protocol','Symbol':'ren'},{'Id':'oracolxor','Symbol':'xor'},{'Id':'funfair','Symbol':'fun'},{'Id':'civic','Symbol':'cvc'},{'Id':'spankchain','Symbol':'spank'},{'Id':'cap','Symbol':'cap'},{'Id':'v-id-blockchain','Symbol':'vidt'},{'Id':'vision','Symbol':'vsn'},{'Id':'libertas-token','Symbol':'libertas'},{'Id':'foam-protocol','Symbol':'foam'},{'Id':'usdq','Symbol':'usdq'},{'Id':'quant-network','Symbol':'qnt'},{'Id':'zinc','Symbol':'zinc'},{'Id':'ghost-by-mcafee','Symbol':'ghost'},{'Id':'key','Symbol':'key'},{'Id':'mini','Symbol':'mini'},{'Id':'mcdex','Symbol':'mcb'},{'Id':'digix-gold','Symbol':'dgx'},{'Id':'binance-usd','Symbol':'busd'},{'Id':'chainlink','Symbol':'link'},{'Id':'daostack','Symbol':'gen'},{'Id':'bzx-protocol','Symbol':'bzrx'},{'Id':'bluzelle','Symbol':'blz'},{'Id':'trust','Symbol':'trust'},{'Id':'livepeer','Symbol':'lpt'},{'Id':'power-ledger','Symbol':'powr'},{'Id':'dether','Symbol':'DTH'},{'Id':'cosplay-token','Symbol':'cot'},{'Id':'deviantcoin','Symbol':'dev'},{'Id':'cdai','Symbol':'cdai'},{'Id':'mybit-token','Symbol':'myb'},{'Id':'seth','Symbol':'seth'},{'Id':'defipie','Symbol':'PIE'},{'Id':'iexec-rlc','Symbol':'rlc'},{'Id':'linkart','Symbol':'lar'},{'Id':'martexcoin','Symbol':'mxt'},{'Id':'jetswap-token','Symbol':'wings'},{'Id':'smart-mfg','Symbol':'mfg'},{'Id':'gnosis','Symbol':'gno'},{'Id':'sirin-labs-token','Symbol':'srn'},{'Id':'bankroll-vault','Symbol':'vlt'},{'Id':'geeq','Symbol':'GEEQ'},{'Id':'unifi-protocol','Symbol':'up'},{'Id':'holotoken','Symbol':'hot'},{'Id':'polytrade','Symbol':'trade'},{'Id':'props','Symbol':'props'},{'Id':'amon','Symbol':'amn'},{'Id':'status','Symbol':'SNT'},{'Id':'boxx','Symbol':'boxx'},{'Id':'morpheus-network','Symbol':'mrph'},{'Id':'dfohub','Symbol':'buidl'},{'Id':'santiment-network-token','Symbol':'san'},{'Id':'robonomics-network','Symbol':'xrt'},{'Id':'ethlend','Symbol':'lend'},{'Id':'measurable-data-token','Symbol':'mdt'},{'Id':'origin-protocol','Symbol':'ogn'},{'Id':'atlantis-token','Symbol':'atis'},{'Id':'remme','Symbol':'rem'},{'Id':'goldmint','Symbol':'mntp'},{'Id':'unibright','Symbol':'ubt'},{'Id':'dia-data','Symbol':'DIA'},{'Id':'reserve-rights-token','Symbol':'rsr'},{'Id':'penta','Symbol':'pnt'},{'Id':'akropolis','Symbol':'akro'},{'Id':'nervenetwork','Symbol':'nvt'},{'Id':'swipe','Symbol':'sxp'},{'Id':'paxos-standard','Symbol':'pax'},{'Id':'request-network','Symbol':'req'},{'Id':'orion-protocol','Symbol':'orn'},{'Id':'real','Symbol':'real'},{'Id':'kleros','Symbol':'pnk'},{'Id':'lock-token','Symbol':'lock'},{'Id':'deipool','Symbol':'dip'},{'Id':'ocean-protocol','Symbol':'ocean'},{'Id':'strong','Symbol':'strong'},{'Id':'polymath-network','Symbol':'poly'},{'Id':'digital-rand','Symbol':'dzar'},{'Id':'eth-rsi-60-40-yield-set','Symbol':'ethrsiapy'},{'Id':'maker','Symbol':'mkr'},{'Id':'usd-coin','Symbol':'usdc'},{'Id':'pundi-x','Symbol':'npxs'},{'Id':'yfii-finance','Symbol':'yfii'},{'Id':'dxdao','Symbol':'dxd'},{'Id':'meta','Symbol':'mta'},{'Id':'metronome','Symbol':'met'},{'Id':'equus-mining-token','Symbol':'eqmt'},{'Id':'stableusd','Symbol':'USDS'},{'Id':'loom-network-new','Symbol':'loom'},{'Id':'agrinovuscoin','Symbol':'agri'},{'Id':'celsius-degree-token','Symbol':'cel'},{'Id':'tokencard','Symbol':'tkn'},{'Id':'transcodium','Symbol':'tns'},{'Id':'ceek','Symbol':'ceek'},{'Id':'compound-0x','Symbol':'czrx'},{'Id':'cryptofranc','Symbol':'xchf'},{'Id':'rocket-pool','Symbol':'rpl'},{'Id':'perlin','Symbol':'perl'},{'Id':'stonk','Symbol':'stonk'},{'Id':'bitsou','Symbol':'btu'},{'Id':'release-ico-project','Symbol':'rel'},{'Id':'balancer','Symbol':'bal'},{'Id':'band-protocol','Symbol':'band'},{'Id':'pangea','Symbol':'xpat'},{'Id':'loopring','Symbol':'lrc'},{'Id':'ink-protocol','Symbol':'xnk'},{'Id':'meter-governance-mapped-by-meter-io','Symbol':'eMTRG'},{'Id':'kardiachain','Symbol':'kai'},{'Id':'storm','Symbol':'stmx'},{'Id':'aelf','Symbol':'elf'},{'Id':'compound-coin','Symbol':'comp'},{'Id':'havven','Symbol':'snx'},{'Id':'aleph','Symbol':'aleph'},{'Id':'weth','Symbol':'weth'},{'Id':'compound-wrapped-btc','Symbol':'cwbtc'},{'Id':'auctus','Symbol':'auc'},{'Id':'lamden','Symbol':'tau'},{'Id':'quadrant-protocol','Symbol':'equad'},{'Id':'trendering','Symbol':'trnd'},{'Id':'gifto','Symbol':'gto'},{'Id':'zzz-finance','Symbol':'zzz'},{'Id':'trustswap','Symbol':'swap'},{'Id':'nectar-token','Symbol':'nec'},{'Id':'anj','Symbol':'anj'},{'Id':'yffi-finance','Symbol':'yffi'},{'Id':'cbi-index-7','Symbol':'cbix7'},{'Id':'machix','Symbol':'mcx'},{'Id':'omisego','Symbol':'omg'},{'Id':'ong','Symbol':'ong'},{'Id':'ampleforth','Symbol':'ampl'},{'Id':'cindicator','Symbol':'cnd'},{'Id':'fintrux','Symbol':'ftx'},{'Id':'dfohub','Symbol':'buidl'},{'Id':'sociall','Symbol':'scl'},{'Id':'pluton','Symbol':'plu'},{'Id':'tether','Symbol':'usdt'},{'Id':'stasis-eurs','Symbol':'eurs'},{'Id':'kyber-network','Symbol':'kncl'},{'Id':'mainframe','Symbol':'mft'},{'Id':'husd','Symbol':'husd'},{'Id':'karma-dao','Symbol':'karma'},{'Id':'rmpl','Symbol':'rmpl'},{'Id':'shipchain','Symbol':'ship'},{'Id':'pillar','Symbol':'plr'},{'Id':'0x','Symbol':'zrx'},{'Id':'2key','Symbol':'2key'},{'Id':'renbtc','Symbol':'renbtc'},{'Id':'melon','Symbol':'mln'},{'Id':'zippie','Symbol':'zipt'},{'Id':'askobar-network','Symbol':'asko'},{'Id':'ethereum-vault','Symbol':'ethv'},{'Id':'finnexus','Symbol':'fnx'},{'Id':'evo','Symbol':'evo'},{'Id':'flixxo','Symbol':'flixx'},{'Id':'pamp-cc','Symbol':'PAMP'},{'Id':'hedgetrade','Symbol':'hedg'},{'Id':'dmst','Symbol':'dmst'},{'Id':'unicrypt','Symbol':'unc'},{'Id':'unipower','Symbol':'power'},{'Id':'metal','Symbol':'mtl'},{'Id':'enjincoin','Symbol':'enj'},{'Id':'compound-usdt','Symbol':'cusdt'},{'Id':'indorse','Symbol':'ind'},{'Id':'antiample','Symbol':'xamp'},{'Id':'ripio-credit-network','Symbol':'rcn'},{'Id':'trueaud','Symbol':'taud'},{'Id':'truegbp','Symbol':'tgbp'},{'Id':'truehkd','Symbol':'thkd'},{'Id':'gastoken','Symbol':'gst2'},{'Id':'chai','Symbol':'chai'},{'Id':'compound-basic-attention-token','Symbol':'cbat'},{'Id':'compound-sai','Symbol':'csai'},{'Id':'compound-ether','Symbol':'ceth'},{'Id':'compound-usd-coin','Symbol':'cusdc'},{'Id':'compound-augur','Symbol':'crep'},{'Id':'leo-token','Symbol':'leo'},{'Id':'huobi-token','Symbol':'ht'},{'Id':'matic-network','Symbol':'matic'},{'Id':'dai','Symbol':'dai'},{'Id':'sai','Symbol':'sai'},{'Id':'nusd','Symbol':'susd'},{'Id':'seur','Symbol':'seur'},{'Id':'ibtc','Symbol':'iBTC'},{'Id':'sbtc','Symbol':'sbtc'},{'Id':'saud','Symbol':'saud'},{'Id':'scex','Symbol':'scex'},{'Id':'sada','Symbol':'sada'},{'Id':'sdash','Symbol':'sdash'},{'Id':'seos','Symbol':'seos'},{'Id':'setc','Symbol':'setc'},{'Id':'sxmr','Symbol':'sxmr'},{'Id':'sxrp','Symbol':'sxrp'},{'Id':'sxag','Symbol':'sxag'},{'Id':'sltc','Symbol':'sltc'},{'Id':'ieth','Symbol':'ieth'},{'Id':'sdefi','Symbol':'sdefi'},{'Id':'sxau','Symbol':'sxau'},{'Id':'sbnb','Symbol':'sbnb'},{'Id':'sxtz','Symbol':'sxtz'},{'Id':'shiba-link','Symbol':'slink'},{'Id':'ibnb-2','Symbol':'ibnb'},{'Id':'ieos','Symbol':'ieos'},{'Id':'dollars','Symbol':'usdx'},{'Id':'true-usd','Symbol':'tusd'},{'Id':'trustline-network','Symbol':'tln'},{'Id':'lunch-money','Symbol':'lmy'},{'Id':'ybusd','Symbol':'ybusd'},{'Id':'ytusd','Symbol':'ytusd'},{'Id':'blockchain-certified-data-token','Symbol':'bcdt'},{'Id':'lendroid-support-token','Symbol':'lst'},{'Id':'marketpeak','Symbol':'peak'},{'Id':'pantos','Symbol':'pan'},{'Id':'gemini-dollar','Symbol':'gusd'},{'Id':'proton','Symbol':'xpr'},{'Id':'keep-network','Symbol':'keep'},{'Id':'renzec','Symbol':'renzec'},{'Id':'renbch','Symbol':'renbch'},{'Id':'t-bitcoin','Symbol':'tbtc'},{'Id':'huobi-btc','Symbol':'hbtc'},{'Id':'shuffle-monster','Symbol':'shuf'},{'Id':'donut','Symbol':'donut'},{'Id':'chi-gastoken','Symbol':'chi'},{'Id':'switch','Symbol':'esh'},{'Id':'pax-gold','Symbol':'paxg'},{'Id':'0xmonero','Symbol':'0xmr'},{'Id':'storj','Symbol':'storj'},{'Id':'salt','Symbol':'salt'},{'Id':'curve-fi-ydai-yusdc-yusdt-ytusd','Symbol':'yCurve'},{'Id':'rarible','Symbol':'rari'},{'Id':'pareto-network','Symbol':'pareto'},{'Id':'plutus-defi','Symbol':'plt'},{'Id':'ptokens-btc','Symbol':'pbtc'},{'Id':'serum','Symbol':'srm'},{'Id':'autonio','Symbol':'niox'},{'Id':'defi-stoa','Symbol':'sta'},{'Id':'falcon-token','Symbol':'fnt'},{'Id':'yam-2','Symbol':'yam'},{'Id':'addax','Symbol':'adx'},{'Id':'curve-dao-token','Symbol':'crv'},{'Id':'darwinia-network-native-token','Symbol':'ring'},{'Id':'cartesi','Symbol':'ctsi'},{'Id':'unilayer','Symbol':'layer'},{'Id':'degenerator','Symbol':'meme'},{'Id':'origintrail','Symbol':'trac'},{'Id':'yam-v2','Symbol':'YAMv2'},{'Id':'jarvis-reward-token','Symbol':'jrt'},{'Id':'neutrino','Symbol':'usdn'},{'Id':'parsiq','Symbol':'prq'},{'Id':'hakka-finance','Symbol':'hakka'},{'Id':'robonomics-web-services','Symbol':'rws'},{'Id':'growth-defi','Symbol':'gro'},{'Id':'concentrated-voting-power','Symbol':'cvp'},{'Id':'ethopt','Symbol':'opt'},{'Id':'sushi','Symbol':'sushi'},{'Id':'stacktical','Symbol':'dsla'},{'Id':'swapfolio','Symbol':'swfl'},{'Id':'fsw-token','Symbol':'fsw'},{'Id':'akropolis-delphi','Symbol':'adel'},{'Id':'swerve-dao','Symbol':'swrv'},{'Id':'multiplier','Symbol':'mxx'},{'Id':'genesis-vision','Symbol':'gvt'},{'Id':'step-finance','Symbol':'step'},{'Id':'safe-coin','Symbol':'safe'},{'Id':'predix-network','Symbol':'prdx'},{'Id':'defipulse-index','Symbol':'dpi'},{'Id':'aavegotchi','Symbol':'ghst'},{'Id':'unicorn-token','Symbol':'uni'},{'Id':'game-x-coin','Symbol':'gxc'},{'Id':'pickle-finance','Symbol':'pickle'},{'Id':'frontier-token','Symbol':'front'},{'Id':'dhedge-dao','Symbol':'dht'},{'Id':'harvest-finance','Symbol':'farm'},{'Id':'golff','Symbol':'gof'},{'Id':'xbtc','Symbol':'xbtc'},{'Id':'origin-dollar','Symbol':'ousd'},{'Id':'aave','Symbol':'aave'},{'Id':'dodo','Symbol':'dodo'},{'Id':'safe2','Symbol':'safe2'},{'Id':'spaceswap-shake','Symbol':'shake'},{'Id':'spaceswap-milk2','Symbol':'milk2'},{'Id':'cvault-finance','Symbol':'core'},{'Id':'perpetual-protocol','Symbol':'perp'},{'Id':'value-liquidity','Symbol':'value'},{'Id':'sparkle','Symbol':'sprkl'},{'Id':'usdk','Symbol':'usdk'},{'Id':'swag-finance','Symbol':'swag'},{'Id':'piedao-dough-v2','Symbol':'dough'},{'Id':'kush-finance','Symbol':'kseed'},{'Id':'ccomp','Symbol':'ccomp'},{'Id':'compound-uniswap','Symbol':'cuni'},{'Id':'quras-token','Symbol':'xqc'},{'Id':'master-usd','Symbol':'musd'},{'Id':'zeroswap','Symbol':'zee'},{'Id':'hegic','Symbol':'hegic'},{'Id':'definer','Symbol':'fin'},{'Id':'astro','Symbol':'astro'},{'Id':'amp-token','Symbol':'amp'},{'Id':'barnbridge','Symbol':'bond'},{'Id':'antcoin','Symbol':'ant'},{'Id':'fuse-network-token','Symbol':'fuse'},{'Id':'empty-set-dollar','Symbol':'esd'},{'Id':'keep3rv1','Symbol':'kp3r'},{'Id':'defidollar','Symbol':'dusd'},{'Id':'aurora-dao','Symbol':'idex'},{'Id':'nix-bridge-token','Symbol':'voice'},{'Id':'hermez-network-token','Symbol':'hez'},{'Id':'surfexutilitytoken','Symbol':'surf'},{'Id':'wrapped-anatha','Symbol':'wanatha'},{'Id':'audius','Symbol':'audio'},{'Id':'atari','Symbol':'atri'},{'Id':'index-cooperative','Symbol':'index'},{'Id':'powertrade-fuel','Symbol':'ptf'},{'Id':'defidollar-dao','Symbol':'dfd'},{'Id':'apy-finance','Symbol':'apy'},{'Id':'geyser','Symbol':'gysr'},{'Id':'keep4r','Symbol':'kp4r'},{'Id':'axie-infinity','Symbol':'axs'},{'Id':'smart-valor','Symbol':'valor'},{'Id':'allianceblock','Symbol':'albt'},{'Id':'tomoe','Symbol':'tomoe'},{'Id':'lua-token','Symbol':'lua'},{'Id':'holyheld','Symbol':'holy'},{'Id':'polkastarter','Symbol':'pols'},{'Id':'rio-defi','Symbol':'rfuel'},{'Id':'unlend-finance','Symbol':'uft'},{'Id':'lgcy-network','Symbol':'lgcy'},{'Id':'rope-token','Symbol':'rope'},{'Id':'plotx','Symbol':'plot'},{'Id':'keysians-network','Symbol':'ken'},{'Id':'nsure-network','Symbol':'nsure'},{'Id':'chronobank','Symbol':'time'},{'Id':'saffron-finance','Symbol':'sfi'},{'Id':'88mph','Symbol':'mph'},{'Id':'oro','Symbol':'oro'},{'Id':'e-radix','Symbol':'exrd'},{'Id':'boosted-finance','Symbol':'boost'},{'Id':'dforce-token','Symbol':'df'},{'Id':'synlev','Symbol':'syn'},{'Id':'lto-network','Symbol':'lto'},{'Id':'synth-soil','Symbol':'soil'},{'Id':'cache-gold','Symbol':'cgt'},{'Id':'nucypher','Symbol':'nu'},{'Id':'octree','Symbol':'oct'},{'Id':'quiverx','Symbol':'qrx'},{'Id':'bitsong','Symbol':'btsg'},{'Id':'radium','Symbol':'val'},{'Id':'api3','Symbol':'api3'},{'Id':'basis-cash','Symbol':'bac'},{'Id':'basis-share','Symbol':'bas'},{'Id':'power-index-pool-token','Symbol':'pipt'},{'Id':'megacryptopolis','Symbol':'mega'},{'Id':'base-protocol','Symbol':'base'},{'Id':'bondly','Symbol':'bondly'},{'Id':'neutrino-system-base-token','Symbol':'nsbt'},{'Id':'nexo','Symbol':'nexo'},{'Id':'aave-aave','Symbol':'aAAVE'},{'Id':'aave-bat','Symbol':'abat'},{'Id':'aave-busd','Symbol':'abusd'},{'Id':'aave-dai','Symbol':'adai'},{'Id':'aave-enj','Symbol':'aenj'},{'Id':'aave-knc','Symbol':'aknc'},{'Id':'aave-link','Symbol':'alink'},{'Id':'aave-mana','Symbol':'amana'},{'Id':'aave-mkr','Symbol':'amkr'},{'Id':'aave-ren','Symbol':'aren'},{'Id':'aave-snx','Symbol':'asnx'},{'Id':'aave-susd','Symbol':'asusd'},{'Id':'aave-tusd','Symbol':'atusd'},{'Id':'aave-uni','Symbol':'auni'},{'Id':'aave-usdc','Symbol':'ausdc'},{'Id':'aave-usdt','Symbol':'ausdt'},{'Id':'aave-wbtc','Symbol':'awbtc'},{'Id':'aave-weth','Symbol':'aweth'},{'Id':'aave-yfi','Symbol':'aYFI'},{'Id':'aave-zrx','Symbol':'azrx'},{'Id':'coinlion','Symbol':'lion'},{'Id':'zlot','Symbol':'zlot'},{'Id':'ecofi','Symbol':'eco'},{'Id':'utrust','Symbol':'utk'},{'Id':'badger-dao','Symbol':'badger'},{'Id':'golden-ratio-token','Symbol':'grt'},{'Id':'lido-dao','Symbol':'ldo'},{'Id':'tornado-cash','Symbol':'torn'},{'Id':'staked-ether','Symbol':'steth'},{'Id':'mahadao','Symbol':'maha'},{'Id':'marlin','Symbol':'pond'},{'Id':'frax-share','Symbol':'fxs'},{'Id':'spice','Symbol':'spice'},{'Id':'1inch','Symbol':'1inch'},{'Id':'plasma-finance','Symbol':'ppay'},{'Id':'mithril-share','Symbol':'mis'},{'Id':'basiscoin-share','Symbol':'bcs'},{'Id':'exeedme','Symbol':'xed'},{'Id':'wozx','Symbol':'wozx'},{'Id':'defi-nation-signals-dao','Symbol':'dsd'},{'Id':'fox-finance','Symbol':'fox'},{'Id':'cover-protocol','Symbol':'cover'},{'Id':'wise-token11','Symbol':'wise'},{'Id':'fera','Symbol':'fera'},{'Id':'furucombo','Symbol':'combo'},{'Id':'usdfreeliquidity','Symbol':'usdfl'},{'Id':'fetch-ai','Symbol':'fet'},{'Id':'pha','Symbol':'pha'},{'Id':'pbtc35a','Symbol':'pbtc35a'},{'Id':'frax','Symbol':'frax'},{'Id':'injective-protocol','Symbol':'inj'},{'Id':'legolas-exchange','Symbol':'lgo'},{'Id':'yield','Symbol':'yld'},{'Id':'cyberfi','Symbol':'cfi'},{'Id':'rari-governance-token','Symbol':'rgt'},{'Id':'rook','Symbol':'rook'},{'Id':'yield-optimization-platform','Symbol':'yop'},{'Id':'nftx','Symbol':'nftx'},{'Id':'robbocoach','Symbol':'rbc'},{'Id':'stake-dao','Symbol':'sdt'},{'Id':'ethos','Symbol':'vgx'},{'Id':'debase','Symbol':'debase'},{'Id':'ankr','Symbol':'ankr'},{'Id':'thorchain','Symbol':'rune'},{'Id':'bao-finance','Symbol':'bao'},{'Id':'reef-finance','Symbol':'reef'},{'Id':'truebit-protocol','Symbol':'tru'},{'Id':'indexed-finance','Symbol':'ndx'},{'Id':'benchmark-protocol','Symbol':'mark'},{'Id':'zero-exchange','Symbol':'zero'},{'Id':'octofi','Symbol':'octo'},{'Id':'oraichain-token','Symbol':'orai'},{'Id':'duckdaodime','Symbol':'ddim'},{'Id':'birdchain','Symbol':'bird'},{'Id':'spacechain','Symbol':'spc'},{'Id':'ramp','Symbol':'ramp'},{'Id':'stabilize','Symbol':'stbz'},{'Id':'insured-finance','Symbol':'infi'},{'Id':'crypto-com-chain','Symbol':'cro'},{'Id':'lukso-token','Symbol':'lyxe'},{'Id':'terra-virtua-kolect','Symbol':'tvk'},{'Id':'digg','Symbol':'digg'},{'Id':'freeliquid','Symbol':'fl'},{'Id':'alpha-finance','Symbol':'alpha'},{'Id':'cudos','Symbol':'cudos'},{'Id':'dexe','Symbol':'dexe'},{'Id':'san-diego-coin','Symbol':'sand'},{'Id':'covir','Symbol':'cvr'},{'Id':'typhoon-cash','Symbol':'phoon'},{'Id':'farmer-defi','Symbol':'frm'},{'Id':'polkabridge','Symbol':'pbr'},{'Id':'snowblossom','Symbol':'snow'},{'Id':'tosdis','Symbol':'dis'},{'Id':'poolz-finance','Symbol':'poolz'},{'Id':'zkswap','Symbol':'zks'},{'Id':'armor','Symbol':'armor'},{'Id':'armor-nxm','Symbol':'arnxm'},{'Id':'opium','Symbol':'opium'},{'Id':'yearn-ecosystem-token-index','Symbol':'yeti'},{'Id':'assy-index','Symbol':'assy'},{'Id':'defi-yield-protocol','Symbol':'dyp'},{'Id':'yusdc-busd-pool','Symbol':'yusdc'},{'Id':'veth2','Symbol':'veth2'},{'Id':'aave-eth-v1','Symbol':'aeth'},{'Id':'cream-eth2','Symbol':'creth2'},{'Id':'fantom','Symbol':'ftm'},{'Id':'prosper','Symbol':'pros'},{'Id':'fastswap','Symbol':'fast'},{'Id':'reflect-finance','Symbol':'rfi'},{'Id':'terrausd','Symbol':'ust'},{'Id':'rendoge','Symbol':'rendoge'},{'Id':'mir-coin','Symbol':'mir'},{'Id':'flex-coin','Symbol':'flex'},{'Id':'metric-exchange','Symbol':'metric'},{'Id':'chartex','Symbol':'chart'},{'Id':'bridge-mutual','Symbol':'bmi'},{'Id':'digitex-futures-exchange','Symbol':'dgtx'},{'Id':'millimeter','Symbol':'mm'},{'Id':'tokenlon','Symbol':'lon'},{'Id':'archer-dao-governance-token','Symbol':'arch'},{'Id':'biblepay','Symbol':'bbp'},{'Id':'sx-network','Symbol':'sx'},{'Id':'lattice-token','Symbol':'ltx'},{'Id':'clash-token','Symbol':'sct'},{'Id':'leverj-gluon','Symbol':'l2'},{'Id':'onix','Symbol':'onx'},{'Id':'beefy-finance','Symbol':'bifi'},{'Id':'stafi','Symbol':'fis'},{'Id':'lina','Symbol':'lina'},{'Id':'oin-finance','Symbol':'oin'},{'Id':'xinchb','Symbol':'xINCHb'},{'Id':'xincha','Symbol':'xINCHa'},{'Id':'crowns','Symbol':'cws'},{'Id':'shiba-inu','Symbol':'shib'},{'Id':'portion','Symbol':'prt'},{'Id':'name-changing-token','Symbol':'nct'},{'Id':'muse-2','Symbol':'muse'},{'Id':'maps','Symbol':'maps'},{'Id':'build-finance','Symbol':'build'},{'Id':'gourmetgalaxy','Symbol':'gum'},{'Id':'defi-top-5-tokens-index','Symbol':'defi5'},{'Id':'cryptocurrency-top-10-tokens-index','Symbol':'cc10'},{'Id':'tixl-new','Symbol':'txl'},{'Id':'razor-network','Symbol':'razor'},{'Id':'strudel-finance','Symbol':'trdl'},{'Id':'yvs-finance','Symbol':'yvs'},{'Id':'bundles','Symbol':'bund'},{'Id':'sashimi','Symbol':'sashimi'},{'Id':'hedget','Symbol':'hget'},{'Id':'option-room','Symbol':'room'},{'Id':'wrapped-crescofin','Symbol':'wcres'},{'Id':'gala','Symbol':'gala'},{'Id':'seigniorage-shares','Symbol':'share'},{'Id':'unistake','Symbol':'unistake'},{'Id':'azuki','Symbol':'azuki'},{'Id':'coin-artist','Symbol':'coin'},{'Id':'dextf','Symbol':'dextf'},{'Id':'mp3','Symbol':'mp3'},{'Id':'litentry','Symbol':'lit'},{'Id':'terra-luna','Symbol':'luna'},{'Id':'easyfi','Symbol':'ez'},{'Id':'sync-network','Symbol':'sync'},{'Id':'finxflo','Symbol':'fxf'},{'Id':'bot-ocean','Symbol':'bots'},{'Id':'mar-network','Symbol':'mars'},{'Id':'nftlootbox','Symbol':'loot'},{'Id':'dlp-duck-token','Symbol':'duck'},{'Id':'the-famous-token','Symbol':'tft'},{'Id':'everid','Symbol':'id'},{'Id':'skale','Symbol':'skl'},{'Id':'dao-maker','Symbol':'dao'},{'Id':'bitenium-token','Symbol':'bt'},{'Id':'flash','Symbol':'flash'},{'Id':'butterfly-protocol-2','Symbol':'bfly'},{'Id':'safedot','Symbol':'sdot'},{'Id':'scomp','Symbol':'scomp'},{'Id':'saave','Symbol':'saave'},{'Id':'idot','Symbol':'idot'},{'Id':'soft-yearn','Symbol':'syfi'},{'Id':'suni','Symbol':'suni'},{'Id':'sren','Symbol':'sren'},{'Id':'umbrella-network','Symbol':'umb'},{'Id':'ichi-farm','Symbol':'ichi'},{'Id':'usdp','Symbol':'usdp'},{'Id':'unisocks','Symbol':'socks'},{'Id':'stsla','Symbol':'stsla'},{'Id':'marginswap','Symbol':'mfi'},{'Id':'envion','Symbol':'evn'},{'Id':'klondike-finance','Symbol':'klon'},{'Id':'klondike-btc','Symbol':'kbtc'},{'Id':'open-governance-token','Symbol':'open'},{'Id':'cryptotask-2','Symbol':'ctask'},{'Id':'pylon-finance','Symbol':'pylon'},{'Id':'peanut','Symbol':'nux'},{'Id':'depay','Symbol':'depay'},{'Id':'fyooz','Symbol':'fyz'},{'Id':'scifi-index','Symbol':'scifi'},{'Id':'0chain','Symbol':'zcn'},{'Id':'unicrypt-2','Symbol':'uncx'},{'Id':'warp-finance','Symbol':'warp'},{'Id':'idle','Symbol':'idle'},{'Id':'sparkpoint','Symbol':'srk'},{'Id':'glitch-protocol','Symbol':'glch'},{'Id':'unimex-network','Symbol':'umx'},{'Id':'whiteheart','Symbol':'white'},{'Id':'dent','Symbol':'dent'},{'Id':'zenfuse','Symbol':'zefu'},{'Id':'moontools','Symbol':'moons'},{'Id':'sake-token','Symbol':'sake'},{'Id':'micro-bitcoin-finance','Symbol':'mbtc'},{'Id':'vesper-finance','Symbol':'vsp'},{'Id':'sharedstake-governance-token','Symbol':'sgt'},{'Id':'shroom-finance','Symbol':'shroom'},{'Id':'gameswap-org','Symbol':'gswap'},{'Id':'fudfinance','Symbol':'fud'},{'Id':'rai','Symbol':'rai'},{'Id':'unidex','Symbol':'unidx'},{'Id':'doki-doki-finance','Symbol':'doki'},{'Id':'essentia','Symbol':'ess'},{'Id':'gather','Symbol':'gth'},{'Id':'offshift','Symbol':'xft'},{'Id':'seen','Symbol':'seen'},{'Id':'ethart','Symbol':'arte'},{'Id':'alpaca','Symbol':'alpa'},{'Id':'utu-coin','Symbol':'utu'},{'Id':'achain-coin','Symbol':'ac'},{'Id':'royale','Symbol':'roya'},{'Id':'premia','Symbol':'premia'},{'Id':'rigel-finance','Symbol':'rigel'},{'Id':'poolcoin','Symbol':'pool'},{'Id':'smartcredit-token','Symbol':'smartcredit'},{'Id':'rootkit','Symbol':'root'},{'Id':'revv','Symbol':'revv'},{'Id':'phoenixdao','Symbol':'phnx'},{'Id':'dexkit','Symbol':'kit'},{'Id':'wootrade-network','Symbol':'woo'},{'Id':'modefi','Symbol':'mod'},{'Id':'hydro','Symbol':'hydro'},{'Id':'mask-network','Symbol':'mask'},{'Id':'anyswap','Symbol':'any'},{'Id':'rally-2','Symbol':'rly'},{'Id':'kira-network','Symbol':'kex'},{'Id':'ultra','Symbol':'uos'},{'Id':'geocoin','Symbol':'geo'},{'Id':'get-token','Symbol':'get'},{'Id':'apoyield','Symbol':'soul'},{'Id':'unifi','Symbol':'unifi'},{'Id':'derivadao','Symbol':'ddx'},{'Id':'quick','Symbol':'quick'},{'Id':'redfox-labs-2','Symbol':'rfox'},{'Id':'monacoin','Symbol':'mona'},{'Id':'hybrix','Symbol':'hy'},{'Id':'supercoin','Symbol':'super'},{'Id':'wrapped-dgld','Symbol':'wdgld'},{'Id':'coinshares-gold-and-cryptoassets-index-lite','Symbol':'cgi'},{'Id':'mushroom','Symbol':'mush'},{'Id':'launchpool','Symbol':'lpool'},{'Id':'xtake','Symbol':'xtk'},{'Id':'signal-token','Symbol':'sig'},{'Id':'hopr','Symbol':'hopr'},{'Id':'foundrydao-logistics','Symbol':'fry'},{'Id':'gamecredits','Symbol':'game'},{'Id':'grap-finance','Symbol':'grap'},{'Id':'render-token','Symbol':'rndr'},{'Id':'ovr','Symbol':'ovr'},{'Id':'mettalex','Symbol':'mtlx'},{'Id':'polkamarkets','Symbol':'polk'},{'Id':'bancor-governance-token','Symbol':'vbnt'},{'Id':'nord-finance','Symbol':'nord'},{'Id':'shadows','Symbol':'dows'},{'Id':'mint-club','Symbol':'mint'},{'Id':'degen-index','Symbol':'degen'},{'Id':'bifrost','Symbol':'bfc'},{'Id':'siren','Symbol':'si'},{'Id':'font','Symbol':'font'},{'Id':'moon','Symbol':'moon'},{'Id':'jupiter','Symbol':'jup'},{'Id':'sentiment-token','Symbol':'sent'},{'Id':'dego-finance','Symbol':'dego'},{'Id':'decentral-games','Symbol':'dg'},{'Id':'sota-finance','Symbol':'sota'},{'Id':'noderunners','Symbol':'ndr'},{'Id':'daofi','Symbol':'daofi'},{'Id':'radicle','Symbol':'rad'},{'Id':'alchemix','Symbol':'alcx'},{'Id':'bankless-dao','Symbol':'bank'},{'Id':'antimatter','Symbol':'matter'},{'Id':'fractal','Symbol':'fcl'},{'Id':'verasity','Symbol':'vra'},{'Id':'nft-index','Symbol':'nfti'},{'Id':'bidipass','Symbol':'bdp'},{'Id':'earnscoin','Symbol':'ern'},{'Id':'kylin-network','Symbol':'kyl'},{'Id':'robot','Symbol':'robot'},{'Id':'etha-lend','Symbol':'etha'},{'Id':'paint','Symbol':'paint'},{'Id':'ruler-protocol','Symbol':'ruler'},{'Id':'xfund','Symbol':'xfund'},{'Id':'balpha','Symbol':'balpha'},{'Id':'dea','Symbol':'dea'},{'Id':'chiliz','Symbol':'chz'},{'Id':'inverse-finance','Symbol':'inv'},{'Id':'govi','Symbol':'govi'},{'Id':'bet-protocol','Symbol':'bepro'},{'Id':'non-fungible-yearn','Symbol':'nfy'},{'Id':'blank','Symbol':'blank'},{'Id':'smol','Symbol':'smol'},{'Id':'definitex','Symbol':'dfx'},{'Id':'b20','Symbol':'b20'},{'Id':'tapmydata','Symbol':'tap'},{'Id':'taco-finance','Symbol':'taco'},{'Id':'fyznft','Symbol':'fyznft'},{'Id':'swgtoken','Symbol':'swg'},{'Id':'dusk-network','Symbol':'dusk'},{'Id':'lcx','Symbol':'lcx'},{'Id':'insurace','Symbol':'insur'},{'Id':'tozex','Symbol':'toz'},{'Id':'visor','Symbol':'visr'},{'Id':'aluna','Symbol':'aln'},{'Id':'chain-guardians','Symbol':'cgg'},{'Id':'crust-network','Symbol':'cru'},{'Id':'my-neighbor-alice','Symbol':'alice'},{'Id':'tower','Symbol':'tower'},{'Id':'polyyield-token','Symbol':'yield'},{'Id':'konomi-network','Symbol':'kono'},{'Id':'soar-2','Symbol':'soar'},{'Id':'dovu','Symbol':'dov'},{'Id':'circleex','Symbol':'cx'},{'Id':'juggernaut','Symbol':'jgn'},{'Id':'hoge-finance','Symbol':'hoge'},{'Id':'changenow','Symbol':'now'},{'Id':'connect-financial','Symbol':'cnfi'},{'Id':'hodltree','Symbol':'htre'},{'Id':'polkafoundry','Symbol':'pkf'},{'Id':'exrt-network','Symbol':'exrt'},{'Id':'deri-protocol','Symbol':'deri'},{'Id':'blockchain-cuties-universe-governance','Symbol':'bcug'},{'Id':'labs-group','Symbol':'labs'},{'Id':'kine-protocol','Symbol':'kine'},{'Id':'hapi','Symbol':'hapi'},{'Id':'k21','Symbol':'k21'},{'Id':'union-protocol-governance-token','Symbol':'unn'},{'Id':'habitat','Symbol':'hbt'},{'Id':'cash-tech','Symbol':'cate'},{'Id':'doraemoon','Symbol':'dora'},{'Id':'sifchain','Symbol':'erowan'},{'Id':'sentivate','Symbol':'sntvt'},{'Id':'chain-games','Symbol':'chain'},{'Id':'xdefi-governance-token','Symbol':'xdex'},{'Id':'upbots','Symbol':'ubxt'},{'Id':'graphlinq-protocol','Symbol':'glq'},{'Id':'lympo','Symbol':'lym'},{'Id':'vidya','Symbol':'vidya'},{'Id':'fireball','Symbol':'fire'},{'Id':'dafi-protocol','Symbol':'dafi'},{'Id':'oddz','Symbol':'oddz'},{'Id':'paypolitan-token','Symbol':'epan'},{'Id':'ara-token','Symbol':'ara'},{'Id':'2gether-2','Symbol':'2gt'},{'Id':'venus-eth','Symbol':'veth'},{'Id':'coinfirm-amlt','Symbol':'amlt'},{'Id':'volentix-vtx','Symbol':'vtx'},{'Id':'vvsp','Symbol':'vvsp'},{'Id':'tribe-2','Symbol':'tribe'},{'Id':'fei-protocol','Symbol':'fei'},{'Id':'xsgd','Symbol':'xsgd'},{'Id':'aioz-network','Symbol':'aioz'},{'Id':'spheroid-universe','Symbol':'sph'},{'Id':'pocmon','Symbol':'pmon'},{'Id':'sylo','Symbol':'sylo'},{'Id':'overline-emblem','Symbol':'emb'},{'Id':'ureeqa','Symbol':'urqa'},{'Id':'linkpool','Symbol':'lpl'},{'Id':'curate','Symbol':'xcur'},{'Id':'cook','Symbol':'cook'},{'Id':'cellframe','Symbol':'cell'},{'Id':'mad-network','Symbol':'mad'},{'Id':'convergence','Symbol':'conv'},{'Id':'swarm','Symbol':'swm'},{'Id':'eddaswap','Symbol':'edda'},{'Id':'tidal-finance','Symbol':'tidal'},{'Id':'deracoin','Symbol':'drc'},{'Id':'xyo-network','Symbol':'xyo'},{'Id':'arcona','Symbol':'arcona'},{'Id':'vulcan-forged','Symbol':'pyr'},{'Id':'roobee','Symbol':'roobee'},{'Id':'deeper-network','Symbol':'dpr'},{'Id':'gains','Symbol':'gains'},{'Id':'liquity-usd','Symbol':'lusd'},{'Id':'equalizer','Symbol':'eqz'},{'Id':'genesis-shards','Symbol':'gs'},{'Id':'internxt','Symbol':'inxt'},{'Id':'olympus','Symbol':'ohm'},{'Id':'raze-network','Symbol':'raze'},{'Id':'alchemist','Symbol':'mist'},{'Id':'cardstarter','Symbol':'cards'},{'Id':'ethbox-token','Symbol':'ebox'},{'Id':'presearch','Symbol':'pre'},{'Id':'ethereum-push-notification-service','Symbol':'push'},{'Id':'zoracles','Symbol':'zora'},{'Id':'boson-protocol','Symbol':'boson'},{'Id':'universal-basic-income','Symbol':'ubi'},{'Id':'total-crypto-market-cap-token','Symbol':'tcap'},{'Id':'basketdao','Symbol':'bask'},{'Id':'nkn','Symbol':'nkn'},{'Id':'the-4th-pillar','Symbol':'four'},{'Id':'dentacoin','Symbol':'dcn'},{'Id':'ampleforth-governance-token','Symbol':'forth'},{'Id':'s1inch','Symbol':'s1inch'},{'Id':'srune','Symbol':'srune'},{'Id':'scrv','Symbol':'scrv'},{'Id':'snflx','Symbol':'snflx'},{'Id':'sfb','Symbol':'sfb'},{'Id':'sgoog','Symbol':'sgoog'},{'Id':'samzn','Symbol':'samzn'},{'Id':'blind-boxes','Symbol':'bles'},{'Id':'stakewise','Symbol':'swise'},{'Id':'kyber-network-crystal','Symbol':'knc'},{'Id':'yaxis','Symbol':'yaxis'},{'Id':'orbs','Symbol':'orbs'},{'Id':'wirex','Symbol':'wxt'},{'Id':'shincoin','Symbol':'scoin'},{'Id':'baguette','Symbol':'bag'},{'Id':'occamfi','Symbol':'occ'},{'Id':'illuvium','Symbol':'ilv'},{'Id':'unfederalreserve','Symbol':'ersdl'},{'Id':'ice-token','Symbol':'ice'},{'Id':'xend-finance','Symbol':'xend'},{'Id':'unmarshal','Symbol':'marsh'},{'Id':'aga-token','Symbol':'aga'},{'Id':'circuits-of-value','Symbol':'coval'},{'Id':'tenset','Symbol':'10set'},{'Id':'bonfi','Symbol':'bnf'},{'Id':'kin','Symbol':'kin'},{'Id':'golem','Symbol':'glm'},{'Id':'telcoin','Symbol':'tel'},{'Id':'unlock-protocol','Symbol':'udt'},{'Id':'pendle','Symbol':'pendle'},{'Id':'waxe','Symbol':'waxe'},{'Id':'coinstarter','Symbol':'stc'},{'Id':'route','Symbol':'route'},{'Id':'nahmii','Symbol':'nii'},{'Id':'paid-network','Symbol':'paid'},{'Id':'keytango','Symbol':'tango'},{'Id':'splyt','Symbol':'shopx'},{'Id':'ares-protocol','Symbol':'ares'},{'Id':'aga-rewards-2','Symbol':'agar'},{'Id':'cryptex-finance','Symbol':'ctx'},{'Id':'shih-tzu','Symbol':'shih'},{'Id':'somidax','Symbol':'smdx'},{'Id':'kishu-inu','Symbol':'kishu'},{'Id':'feg-token','Symbol':'feg'},{'Id':'stobox-token','Symbol':'stbu'},{'Id':'o3-swap','Symbol':'o3'},{'Id':'woofy','Symbol':'woofy'},{'Id':'shibaken-finance','Symbol':'shibaken'},{'Id':'kirobo','Symbol':'kiro'},{'Id':'convex-finance','Symbol':'cvx'},{'Id':'seedswap','Symbol':'snft'},{'Id':'8pay','Symbol':'8pay'},{'Id':'game','Symbol':'gtc'},{'Id':'graviton','Symbol':'gton'},{'Id':'alchemix-usd','Symbol':'alusd'},{'Id':'sarcophagus','Symbol':'sarco'},{'Id':'terablock','Symbol':'tbc'},{'Id':'value-usd','Symbol':'vusd'},{'Id':'hokkaidu-inu','Symbol':'hokk'},{'Id':'boringdao-[old]','Symbol':'bor'},{'Id':'zoo-token','Symbol':'zoot'},{'Id':'dogelon-mars','Symbol':'elon'},{'Id':'superbid','Symbol':'superbid'},{'Id':'nft-tone','Symbol':'tone'},{'Id':'smartkey','Symbol':'skey'},{'Id':'nimbus','Symbol':'nbu'},{'Id':'leash','Symbol':'leash'},{'Id':'district0x','Symbol':'dnt'},{'Id':'defi-factory-token','Symbol':'deft'},{'Id':'dfyn-network','Symbol':'dfyn'},{'Id':'metaverse-index','Symbol':'mvi'},{'Id':'akita-inu','Symbol':'akita'},{'Id':'liquity','Symbol':'lqty'},{'Id':'verox','Symbol':'vrx'},{'Id':'baby-bitcoin','Symbol':'bbtc'},{'Id':'munch-token','Symbol':'munch'},{'Id':'bezoge-earth','Symbol':'bezoge'},{'Id':'island-coin','Symbol':'isle'},{'Id':'bitcashpay','Symbol':'bcp'},{'Id':'ethereummax','Symbol':'emax'},{'Id':'instadapp','Symbol':'inst'},{'Id':'cavapoo','Symbol':'cava'},{'Id':'swapp','Symbol':'swapp'},{'Id':'dvision-network','Symbol':'dvi'},{'Id':'cad-coin','Symbol':'cadc'},{'Id':'arc-governance','Symbol':'arcx'},{'Id':'amun-defi-index','Symbol':'dfi'},{'Id':'amun-defi-momentum-index','Symbol':'dmx'},{'Id':'xsushi','Symbol':'xsushi'},{'Id':'nxm','Symbol':'nxm'},{'Id':'unit-protocol','Symbol':'col'},{'Id':'auction','Symbol':'auction'},{'Id':'singularitynet','Symbol':'agix'},{'Id':'olyseum','Symbol':'oly'},{'Id':'unizen','Symbol':'zcx'},{'Id':'fnkcom','Symbol':'fnk'},{'Id':'gerowallet','Symbol':'gero'},{'Id':'unobtanium','Symbol':'uno', 'Id': 'gmx', 'Symbol': 'GMX'}, {'Id':'usdc', 'Symbol':'USDC'}]"; + + public static List GetTokens() + { + return JsonConvert.DeserializeObject(TokensJSON).Tokens; + } + + public static List GetGeckoTokens() + { + return JsonConvert.DeserializeObject>(GeckoJson); + } + + public static GeckoToken GetGeckoToken(string ticker) + { + var geckoToken = GetGeckoTokens(); + return geckoToken.FirstOrDefault(t => string.Equals(t.Symbol, ticker, StringComparison.CurrentCultureIgnoreCase)); + } + + public static string GetContractAddress(Ticker ticker) => ticker switch + { + Ticker.BTC => Arbitrum.Address.WBTC, + Ticker.ETH => Arbitrum.Address.ETH, + Ticker.LINK => Arbitrum.Address.LINK, + Ticker.UNI => Arbitrum.Address.UNI, + Ticker.GMX => Arbitrum.Address.GMX, + Ticker.USDT => Arbitrum.Address.USDT, + Ticker.USDC => Arbitrum.Address.USDC, + _ => Arbitrum.Address.Zero + }; + + public static int GetDecimal(Ticker ticker) => ticker switch + { + Ticker.BTC => 8, + Ticker.ETH => 18, + Ticker.LINK => 18, + Ticker.UNI => 18, + Ticker.GMX => 18, + Ticker.USDT => 6, + Ticker.USDC => 6, + _ => throw new Exception($"No decimal for {ticker}") + }; + + public static Ticker GetTicker(string address) => address.ToLowerInvariant() switch + { + Arbitrum.Address.WBTC => Ticker.BTC, + Arbitrum.Address.ETH => Ticker.ETH, + Arbitrum.Address.LINK => Ticker.LINK, + Arbitrum.Address.UNI => Ticker.UNI, + Arbitrum.Address.GMX => Ticker.GMX, + Arbitrum.Address.USDT => Ticker.USDT, + Arbitrum.Address.USDC => Ticker.USDC, + _ => throw new NotImplementedException(), + }; +} diff --git a/src/Managing.Infrastructure.Web3/Services/TradaoService.cs b/src/Managing.Infrastructure.Web3/Services/TradaoService.cs new file mode 100644 index 0000000..6d8e0a6 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Services/TradaoService.cs @@ -0,0 +1,98 @@ +using Managing.Application.Abstractions.Services; +using Managing.Domain.Statistics; +using Managing.Domain.Trades; +using Managing.Infrastructure.Evm.Models; +using System.Net.Http.Json; + +namespace Managing.Infrastructure.Evm.Services; + +public class TradaoService : ITradaoService +{ + private readonly HttpClient _httpClient; + + public TradaoService() + { + _httpClient = new HttpClient(); ; + } + + public async Task> GetBadTrader() + { + var bestTraders = await _httpClient.GetFromJsonAsync($"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/asc/2592000/0/?current=1&limit=500&order=asc&window=2592000&chain=42161&exchange=gmx&openPosition=0"); + + if (bestTraders == null || bestTraders.row.Count == 0) + { + return new List(); + } + + return await GetTraderDetails(bestTraders); + } + + + public async Task> GetBestTrader() + { + var bestTraders = await _httpClient.GetFromJsonAsync($"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/desc/2592000/0/?current=1&limit=500&order=desc&window=2592000&chain=42161&exchange=gmx&openPosition=0"); + + if (bestTraders == null || bestTraders.row.Count == 0) + { + return new List(); + } + + return await GetTraderDetails(bestTraders); + } + + public async Task> GetTrades(string address) + { + var response = await _httpClient.GetFromJsonAsync($"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{address}"); + + var trades = new List(); + + if (response == null) return trades; + + foreach (var position in response.openPositions) + { + var trade = new Trade( + DateTime.UtcNow, + position.isLong ? Common.Enums.TradeDirection.Long : Common.Enums.TradeDirection.Short, + Common.Enums.TradeStatus.Filled, + Common.Enums.TradeType.Market, + TokenService.GetTicker(position.indexTokenAddress), + Convert.ToDecimal(position.collateral), + Convert.ToDecimal(position.averagePrice), + Convert.ToDecimal(position.position) / Convert.ToDecimal(position.collateral), + address, position.liqPrice + ); + + trades.Add(trade); + } + + return trades; + } + + private async Task> GetTraderDetails(TradaoList traders) + { + var result = new List(); + foreach (var trader in traders.row) + { + var response = await _httpClient.GetFromJsonAsync($"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{trader.user}"); + + if (response != null) + result.Add(Map(response, trader.user)); + } + + return result; + } + + private Trader Map(TradaoUserDetails response, string address) + { + return new Trader + { + Address = address, + Winrate = (int)(response.summary.winRate * 100), + Pnl = Convert.ToDecimal(response.summary.pnl), + TradeCount = response.summary.trades, + AverageWin = Convert.ToDecimal(response.summary.averageWin), + AverageLoss = Convert.ToDecimal(response.summary.averageLoss), + Roi = Convert.ToDecimal(response.summary.roi) + }; + } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Chainlink.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Chainlink.cs new file mode 100644 index 0000000..58a4baf --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Chainlink.cs @@ -0,0 +1,122 @@ +using GraphQL.Client.Abstractions; +using GraphQL; +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Core; +using Managing.Infrastructure.Evm.Subgraphs.Models; +using Managing.Common; +using Managing.Domain.Candles; +using Managing.Infrastructure.Evm.Extensions; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Subgraphs; + +public class Chainlink : ISubgraphPrices +{ + SubgraphProvider ISubgraphPrices.GetProvider() => SubgraphProvider.ChainlinkPrice; + + private readonly IGraphQLClient _graphQLClient; + private readonly string _baseToken = "/USD"; + + public Chainlink(IGraphQLClient graphQLHttpClient) + { + _graphQLClient = graphQLHttpClient ?? throw new ArgumentNullException(nameof(graphQLHttpClient)); + } + + public async Task> GetPrices(Ticker ticker, DateTime startDate, Timeframe timeframe) + { + var path = ticker.ToString() + _baseToken; + var batchSize = 1000; + var batchMax = 6; + var priceRounds = new List(); + var feedCondition = $@"{{ assetPair: ""{path}"" }}"; + + // Fetching prices from graphql ticker + for (int i = 0; i < batchMax; i++) + { + var query = $"{{ prices(first: {batchSize}, skip: {i * batchSize}, orderBy: timestamp, orderDirection: desc, where: {feedCondition} ) {{ timestamp,price}} }}"; + var graphQuery = new GraphQLRequest + { + Query = query + }; + + var response = await _graphQLClient.SendQueryAsync(graphQuery); + priceRounds.AddRange(response.Data.Prices); + } + + var rounds = new List(); + + // Format response + foreach (var round in priceRounds) + { + var timestamp = int.Parse(round.Timestamp); + rounds.Add(new Round + { + UnixTimestamp = timestamp, + Value = (double.Parse(round.Price) / 1e8).ToString(), + Date = DateHelpers.GetFromUnixTimestamp(timestamp) + }); + } + + rounds.Sort((timeA, timeB) => timeA.UnixTimestamp - timeB.UnixTimestamp); + + return rounds.ToArray().GetCandles(timeframe, ticker); + } + + public Task GetVolume(Ticker ticker) + { + //var query = $"{{ assetPairs() {{ id }} }}"; + //var graphQuery = new GraphQLRequest + //{ + // Query = query + //}; + + //var response = await _graphQLClient.SendQueryAsync(graphQuery); + throw new NotImplementedException(); + } + + public async Task> GetTickers() + { + var batchSize = 100; + var batchMax = 10; + var tickers = new List(); + + for (int i = 0; i < batchMax; i++) + { + var query = $"{{ assetPairs(first: {batchSize}, skip: {i * batchSize}) {{ id }} }}"; + var graphQuery = new GraphQLRequest + { + Query = query + }; + + var response = await _graphQLClient.SendQueryAsync(graphQuery); + + if (response.Data?.AssetPairs != null) + { + tickers.AddRange(ParseTickers(response.Data.AssetPairs)); + } + } + + return tickers; + } + + private List ParseTickers(List pairs) + { + var tickers = new List(); + foreach (var pair in pairs) + { + var items = pair.Id.Split('/'); + + if (items.Length == 2 && items[1] == Constants.Stablecoins.Usd) + { + try + { + var ticker = MiscExtensions.ParseEnum(items[0]); + tickers.Add(ticker); + } + catch (Exception ex) { } + } + } + + return tickers; + } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/ChainlinkGmx.cs b/src/Managing.Infrastructure.Web3/Subgraphs/ChainlinkGmx.cs new file mode 100644 index 0000000..5d8c7d0 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/ChainlinkGmx.cs @@ -0,0 +1,92 @@ +using GraphQL.Client.Abstractions; +using GraphQL; +using Managing.Core; +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Infrastructure.Evm.Subgraphs.Models; +using Managing.Domain.Candles; +using Managing.Infrastructure.Evm.Extensions; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Subgraphs; + +public class ChainlinkGmx : ISubgraphPrices +{ + SubgraphProvider ISubgraphPrices.GetProvider() => SubgraphProvider.ChainlinkGmx; + + private readonly IGraphQLClient _graphQLClient; + private readonly string _baseToken = "_USD"; + private Dictionary _feeds = new Dictionary() + { + {"BTC_USD", "0xae74faa92cb67a95ebcab07358bc222e33a34da7" }, + {"ETH_USD", "0x37bc7498f4ff12c19678ee8fe19d713b87f6a9e6" }, + {"BNB_USD", "0xc45ebd0f901ba6b2b8c7e70b717778f055ef5e6d" }, + {"LINK_USD", "0xdfd03bfc3465107ce570a0397b247f546a42d0fa" }, + {"UNI_USD", "0x68577f915131087199fe48913d8b416b3984fd38" }, + {"SUSHI_USD", "0x7213536a36094cd8a768a5e45203ec286cba2d74" }, + {"AVAX_USD", "0x0fc3657899693648bba4dbd2d8b33b82e875105d" }, + {"AAVE_USD", "0xe3f0dede4b499c07e12475087ab1a084b5f93bc0" }, + {"YFI_USD", "0x8a4d74003870064d41d4f84940550911fbfccf04" }, + {"SPELL_USD", "0x8640b23468815902e011948f3ab173e1e83f9879" }, + }; + + public ChainlinkGmx(IGraphQLClient graphQLHttpClient) + { + _graphQLClient = graphQLHttpClient ?? throw new ArgumentNullException(nameof(graphQLHttpClient)); + } + + + public async Task> GetPrices(Ticker ticker, DateTime startDate, Timeframe timeframe) + { + var path = ticker.ToString() + _baseToken; + var feed = _feeds.GetValueOrDefault(path); + var perChunk = 1000; + var totalChunk = 6; + var priceRounds = new List(); + var feedCondition = $@"{{ feed: ""{feed}"" }}"; + + for (int i = 0; i < totalChunk; i++) + { + var query = $"{{ rounds(first: {perChunk}, skip: {i * perChunk}, orderBy: unixTimestamp, orderDirection: desc, where: {feedCondition} ) {{ unixTimestamp,value}} }}"; + var graphQuery = new GraphQLRequest + { + Query = query + }; + + var response = await _graphQLClient.SendQueryAsync(graphQuery); + priceRounds.AddRange(response.Data.Rounds); + } + + var rounds = new List(); + var uniqTs = new HashSet(); + + foreach (var round in priceRounds) + { + if (uniqTs.Contains(round.UnixTimestamp)) + { + continue; + } + + uniqTs.Add(round.UnixTimestamp); + rounds.Add(new Round + { + UnixTimestamp = round.UnixTimestamp, + Value = (double.Parse(round.Value) / 1e8).ToString(), + Date = DateHelpers.GetFromUnixTimestamp(round.UnixTimestamp) + }); + } + + rounds.Sort((timeA, timeB) => timeA.UnixTimestamp - timeB.UnixTimestamp); + + return rounds.ToArray().GetCandles(timeframe, ticker); + } + + public Task GetVolume(Ticker ticker) + { + throw new NotImplementedException(); + } + + public Task> GetTickers() + { + throw new NotImplementedException(); + } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Gbc.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Gbc.cs new file mode 100644 index 0000000..f726eb4 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Gbc.cs @@ -0,0 +1,104 @@ +using GraphQL; +using GraphQL.Client.Abstractions; +using Managing.Core; +using Managing.Domain.Candles; +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Infrastructure.Evm.Services; +using Managing.Infrastructure.Evm.Subgraphs.Models; +using NBitcoin; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Subgraphs; + +public class Gbc : ISubgraphPrices +{ + private readonly IGraphQLClient _graphQLClient; + public SubgraphProvider GetProvider() => SubgraphProvider.Gbc; + + public Gbc(IGraphQLClient graphQLHttpClient) + { + _graphQLClient = graphQLHttpClient ?? throw new ArgumentNullException(nameof(graphQLHttpClient)); + } + + public async Task> GetPrices(Ticker ticker, DateTime startDate, Timeframe timeframe) + { + var batchSize = 1000; + var batchMax = 6; + var priceRounds = new List(); + var tickerContract = TokenService.GetContractAddress(ticker); + var unixTimeframe = timeframe.GetUnixInterval(); + var start = startDate.ToUnixTimestamp(); + var end = DateTime.UtcNow.ToUnixTimestamp(); + var feedCondition = $@"{{ tokenAddress: ""_{tickerContract}"", interval: ""_{unixTimeframe}"", timestamp_gte: {start}, timestamp_lte: {end} }}"; + + // Fetching prices from graphql ticker + for (int i = 0; i < batchMax; i++) + { + var query = $"{{ pricefeeds(first: {batchSize}, skip: {i * batchSize}, orderBy: timestamp, orderDirection: desc, where: {feedCondition} ) {{ timestamp,o,h,l,c}} }}"; + var graphQuery = new GraphQLRequest + { + Query = query + }; + + var response = await _graphQLClient.SendQueryAsync(graphQuery); + priceRounds.AddRange(response.Data.PriceFeeds); + } + + priceRounds.Sort((timeA, timeB) => timeA.Timestamp - timeB.Timestamp); + + var candles = new List(); + + var firstRound = priceRounds.FirstOrDefault(); + if (firstRound == null) + return candles; + + var previousCandle = BuildCandle(firstRound, ticker, timeframe); + + // Format response + foreach (var price in priceRounds.Skip(1)) + { + var candle = BuildCandle(price, ticker, timeframe); + candle.OpenTime = previousCandle.Date; + candles.Add(candle); + } + + return candles; + } + + private Candle BuildCandle(GbcPrice ohlc, Ticker ticker, Timeframe timeframe) + { + return new Candle() + { + Date = DateHelpers.GetFromUnixTimestamp(ohlc.Timestamp), + Open = FormatPrice(ohlc.O), + High = FormatPrice(ohlc.H), + Low = FormatPrice(ohlc.L), + Close = FormatPrice(ohlc.C), + Exchange = TradingExchanges.Evm, + Ticker = ticker.ToString(), + Timeframe = timeframe + }; + } + + private static decimal FormatPrice(string price) + { + return (decimal)(double.Parse(price) / 1e30); + } + + public Task GetVolume(Ticker ticker) + { + throw new NotImplementedException(); + } + + public Task> GetTickers() + { + var tickers = new List() { + Ticker.BTC, + Ticker.LINK, + Ticker.ETH, + Ticker.UNI + }; + + return Task.FromResult(tickers.AsEnumerable()); + } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/GbcPrices.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/GbcPrices.cs new file mode 100644 index 0000000..f5008ce --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/GbcPrices.cs @@ -0,0 +1,15 @@ +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class GbcPrices +{ + public List PriceFeeds { get; set; } +} + +public class GbcPrice +{ + public int Timestamp { get; set; } + public string O { get; set; } + public string H { get; set; } + public string L { get; set; } + public string C { get; set; } +} \ No newline at end of file diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/Pair.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Pair.cs new file mode 100644 index 0000000..60f52d7 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Pair.cs @@ -0,0 +1,11 @@ +using Nethereum.Contracts.Standards.ERC20.TokenList; + +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class Pair +{ + public string ReserveETH { get; set; } + public string ReserveUSD { get; set; } + public Token Token0 { get; set; } + public Token Token1 { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/Pools.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Pools.cs new file mode 100644 index 0000000..5bc5fd2 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Pools.cs @@ -0,0 +1,6 @@ +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class Pools +{ + public List Pairs { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/Price.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Price.cs new file mode 100644 index 0000000..973b344 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Price.cs @@ -0,0 +1,17 @@ +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class ChainlinkPrice +{ + public string Price { get; set; } + public string Timestamp { get; set; } +} + +public class ChainlinkAssetPairs +{ + public List AssetPairs { get; set; } +} + +public class AssetPair +{ + public string Id { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/PricesChainlink.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/PricesChainlink.cs new file mode 100644 index 0000000..9117571 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/PricesChainlink.cs @@ -0,0 +1,6 @@ +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class ChainlinkPrices +{ + public List Prices { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/Round.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Round.cs new file mode 100644 index 0000000..d90063c --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Round.cs @@ -0,0 +1,8 @@ +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class Round +{ + public int UnixTimestamp { get; set; } + public DateTime Date { get; set; } + public string Value { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/Rounds.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Rounds.cs new file mode 100644 index 0000000..93db593 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/Rounds.cs @@ -0,0 +1,6 @@ +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class PriceRoundsChainlinkGmx +{ + public List Rounds { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/TokenDetails.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/TokenDetails.cs new file mode 100644 index 0000000..62a7636 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/TokenDetails.cs @@ -0,0 +1,20 @@ +using Nethereum.Contracts.Standards.ERC20.TokenList; +using System.Text.Json.Serialization; + +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class TokenDetails : Token +{ + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string TradeVolume { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string TradeVolumeUSD { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string TotalSupply { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string TotalLiquidity { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Models/TopTokens.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Models/TopTokens.cs new file mode 100644 index 0000000..6c84639 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Models/TopTokens.cs @@ -0,0 +1,6 @@ +namespace Managing.Infrastructure.Evm.Subgraphs.Models; + +public class TopTokens +{ + public List Tokens { get; set; } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/SubgraphExtensions.cs b/src/Managing.Infrastructure.Web3/Subgraphs/SubgraphExtensions.cs new file mode 100644 index 0000000..f179275 --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/SubgraphExtensions.cs @@ -0,0 +1,41 @@ +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Infrastructure.Evm.Services; +using Microsoft.Extensions.DependencyInjection; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Subgraphs; + +public static class SubgraphExtensions +{ + public static void AddUniswapV2(this IServiceCollection services) + { + services.AddSingleton(ctx => + { + return new Uniswap(SubgraphService.GetSubgraphClient(SubgraphProvider.UniswapV2)); + }); + } + + public static void AddChainlink(this IServiceCollection services) + { + services.AddSingleton(ctx => + { + return new Chainlink(SubgraphService.GetSubgraphClient(SubgraphProvider.ChainlinkPrice)); + }); + } + + public static void AddGbcFeed(this IServiceCollection services) + { + services.AddSingleton(ctx => + { + return new Gbc(SubgraphService.GetSubgraphClient(SubgraphProvider.Gbc)); + }); + } + + public static void AddChainlinkGmx(this IServiceCollection services) + { + services.AddSingleton(ctx => + { + return new ChainlinkGmx(SubgraphService.GetSubgraphClient(SubgraphProvider.ChainlinkGmx)); + }); + } +} diff --git a/src/Managing.Infrastructure.Web3/Subgraphs/Uniswap.cs b/src/Managing.Infrastructure.Web3/Subgraphs/Uniswap.cs new file mode 100644 index 0000000..5f78f5a --- /dev/null +++ b/src/Managing.Infrastructure.Web3/Subgraphs/Uniswap.cs @@ -0,0 +1,85 @@ +using GraphQL.Client.Abstractions; +using GraphQL; +using Managing.Infrastructure.Evm.Abstractions; +using Managing.Infrastructure.Evm.Subgraphs.Models; +using Managing.Domain.Candles; +using static Managing.Common.Enums; + +namespace Managing.Infrastructure.Evm.Subgraphs; + +public class Uniswap : IUniswap +{ + SubgraphProvider ISubgraphPrices.GetProvider() => SubgraphProvider.UniswapV2; + + private readonly IGraphQLClient _graphQLClient; + + public Uniswap(IGraphQLClient graphQLHttpClient) + { + _graphQLClient = graphQLHttpClient ?? throw new ArgumentNullException(nameof(graphQLHttpClient)); + } + + /// + /// Get the first 150 most liquid market pairs ordered by desc + /// + /// + public async Task GetMostLiquidMarketPairs() + { + var query = new GraphQLRequest + { + Query = @" + { + pairs(first: 150, orderBy: reserveETH orderDirection: desc){ + token0 { + symbol + } + token1 { + symbol + } + reserveETH + reserveUSD + } + } + " + }; + + GraphQLResponse response = await _graphQLClient.SendQueryAsync(query); + return response.Data; + } + + public async Task GetTopTokens() + { + var query = new GraphQLRequest + { + Query = @" + { + tokens (first: 150, orderBy: tradeVolumeUSD orderDirection: desc){ + symbol + name + tradeVolume + tradeVolumeUSD + totalSupply + totalLiquidity + } + } + " + }; + + GraphQLResponse response = await _graphQLClient.SendQueryAsync(query); + return response.Data; + } + + public Task> GetPrices(Ticker ticker, DateTime startDate, Timeframe timeframe) + { + throw new NotImplementedException(); + } + + public Task GetVolume(Ticker ticker) + { + throw new NotImplementedException(); + } + + public Task> GetTickers() + { + throw new NotImplementedException(); + } +} diff --git a/src/Managing.Infrastructure.Worker/Managing.Infrastructure.Worker.csproj b/src/Managing.Infrastructure.Worker/Managing.Infrastructure.Worker.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/src/Managing.Infrastructure.Worker/Managing.Infrastructure.Worker.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/Managing.Infrastructure.Worker/WorkerService.cs b/src/Managing.Infrastructure.Worker/WorkerService.cs new file mode 100644 index 0000000..768a69a --- /dev/null +++ b/src/Managing.Infrastructure.Worker/WorkerService.cs @@ -0,0 +1,8 @@ +using System; + +namespace Managing.Infrastructure.Worker +{ + public class Class1 + { + } +} diff --git a/src/Managing.Tools.ABI/Gmx/core/OrderBook.abi b/src/Managing.Tools.ABI/Gmx/core/OrderBook.abi new file mode 100644 index 0000000..d354d58 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/OrderBook.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"CancelDecreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"purchaseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"purchaseTokenAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"CancelIncreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"triggerRatio","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"bool","name":"shouldUnwrap","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"CancelSwapOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"CreateDecreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"purchaseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"purchaseTokenAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"CreateIncreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"triggerRatio","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"bool","name":"shouldUnwrap","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"CreateSwapOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionPrice","type":"uint256"}],"name":"ExecuteDecreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"purchaseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"purchaseTokenAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionPrice","type":"uint256"}],"name":"ExecuteIncreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"triggerRatio","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"bool","name":"shouldUnwrap","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"ExecuteSwapOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"router","type":"address"},{"indexed":false,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"address","name":"weth","type":"address"},{"indexed":false,"internalType":"address","name":"usdg","type":"address"},{"indexed":false,"internalType":"uint256","name":"minExecutionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minPurchaseTokenAmountUsd","type":"uint256"}],"name":"Initialize","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"}],"name":"UpdateDecreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gov","type":"address"}],"name":"UpdateGov","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"}],"name":"UpdateIncreaseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minExecutionFee","type":"uint256"}],"name":"UpdateMinExecutionFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minPurchaseTokenAmountUsd","type":"uint256"}],"name":"UpdateMinPurchaseTokenAmountUsd","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"ordexIndex","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"triggerRatio","type":"uint256"},{"indexed":false,"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"indexed":false,"internalType":"bool","name":"shouldUnwrap","type":"bool"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"UpdateSwapOrder","type":"event"},{"inputs":[],"name":"PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDG_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"}],"name":"cancelDecreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"}],"name":"cancelIncreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_swapOrderIndexes","type":"uint256[]"},{"internalType":"uint256[]","name":"_increaseOrderIndexes","type":"uint256[]"},{"internalType":"uint256[]","name":"_decreaseOrderIndexes","type":"uint256[]"}],"name":"cancelMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"}],"name":"cancelSwapOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_triggerPrice","type":"uint256"},{"internalType":"bool","name":"_triggerAboveThreshold","type":"bool"}],"name":"createDecreaseOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_triggerPrice","type":"uint256"},{"internalType":"bool","name":"_triggerAboveThreshold","type":"bool"},{"internalType":"uint256","name":"_executionFee","type":"uint256"},{"internalType":"bool","name":"_shouldWrap","type":"bool"}],"name":"createIncreaseOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_triggerRatio","type":"uint256"},{"internalType":"bool","name":"_triggerAboveThreshold","type":"bool"},{"internalType":"uint256","name":"_executionFee","type":"uint256"},{"internalType":"bool","name":"_shouldWrap","type":"bool"},{"internalType":"bool","name":"_shouldUnwrap","type":"bool"}],"name":"createSwapOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"decreaseOrders","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"internalType":"address","name":"indexToken","type":"address"},{"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"decreaseOrdersIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"address payable","name":"_feeReceiver","type":"address"}],"name":"executeDecreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"address payable","name":"_feeReceiver","type":"address"}],"name":"executeIncreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"address payable","name":"_feeReceiver","type":"address"}],"name":"executeSwapOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_orderIndex","type":"uint256"}],"name":"getDecreaseOrder","outputs":[{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"internalType":"address","name":"indexToken","type":"address"},{"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_orderIndex","type":"uint256"}],"name":"getIncreaseOrder","outputs":[{"internalType":"address","name":"purchaseToken","type":"address"},{"internalType":"uint256","name":"purchaseTokenAmount","type":"uint256"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"indexToken","type":"address"},{"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_orderIndex","type":"uint256"}],"name":"getSwapOrder","outputs":[{"internalType":"address","name":"path0","type":"address"},{"internalType":"address","name":"path1","type":"address"},{"internalType":"address","name":"path2","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"triggerRatio","type":"uint256"},{"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"internalType":"bool","name":"shouldUnwrap","type":"bool"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_otherToken","type":"address"}],"name":"getUsdgMinPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"increaseOrders","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"purchaseToken","type":"address"},{"internalType":"uint256","name":"purchaseTokenAmount","type":"uint256"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"indexToken","type":"address"},{"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"triggerPrice","type":"uint256"},{"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"increaseOrdersIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"},{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_usdg","type":"address"},{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"},{"internalType":"uint256","name":"_minPurchaseTokenAmountUsd","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minExecutionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minPurchaseTokenAmountUsd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"}],"name":"setMinExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minPurchaseTokenAmountUsd","type":"uint256"}],"name":"setMinPurchaseTokenAmountUsd","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"swapOrders","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"triggerRatio","type":"uint256"},{"internalType":"bool","name":"triggerAboveThreshold","type":"bool"},{"internalType":"bool","name":"shouldUnwrap","type":"bool"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"swapOrdersIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"uint256","name":"_triggerPrice","type":"uint256"},{"internalType":"bool","name":"_triggerAboveThreshold","type":"bool"}],"name":"updateDecreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"uint256","name":"_triggerPrice","type":"uint256"},{"internalType":"bool","name":"_triggerAboveThreshold","type":"bool"}],"name":"updateIncreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_triggerRatio","type":"uint256"},{"internalType":"bool","name":"_triggerAboveThreshold","type":"bool"}],"name":"updateSwapOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdg","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_triggerAboveThreshold","type":"bool"},{"internalType":"uint256","name":"_triggerPrice","type":"uint256"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_maximizePrice","type":"bool"},{"internalType":"bool","name":"_raise","type":"bool"}],"name":"validatePositionOrderPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_triggerRatio","type":"uint256"}],"name":"validateSwapOrderPriceWithTriggerAboveThreshold","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/OrderBook.bin b/src/Managing.Tools.ABI/Gmx/core/OrderBook.bin new file mode 100644 index 0000000..b433565 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/OrderBook.bin @@ -0,0 +1 @@ +6080604052600e805460ff1916905534801561001a57600080fd5b506001600055600780546001600160a01b031916331790556153c4806100416000396000f3fe6080604052600436106101c35760003560e01c8062cf066b1461022a578063026032ee1461026f57806307c7edc3146102fa5780630d5cc9381461033d57806311d9444a1461036757806312d43a51146103aa578063269ae6c2146103db5780632b7d6290146104a6578063392e53cd146105465780633fc8cef31461056f57806347e0bbd0146105845780634a686d67146105ae5780634c54f0b0146105c357806363ae21031461062d57806379221fa214610642578063807c5600146106c15780638de10c2e1461087357806395082d25146108885780639983ee1b1461089d5780639e23de5c146108db5780639e71b0f01461090e578063a397ea5414610938578063aec224551461097c578063b142a4b0146109af578063c16cde8a14610a99578063c4a1821b14610ae9578063c86b0f7d14610b99578063cfad57a214610bd7578063d0d40cd614610c0a578063d38ab51914610c9d578063d3bab1d114610ce0578063d566d0ca14610d74578063d7c41c7914610da7578063f2d2e01b14610e00578063f5b91b7b14610e94578063f882ac0714610ea9578063f887ea4014610ed3578063fbfa77cf14610ee8578063fc2cee6214610efd57610225565b36610225576008546001600160a01b03163314610223576040805162461bcd60e51b815260206004820152601960248201527827b93232b92137b7b59d1034b73b30b634b21039b2b73232b960391b604482015290519081900360640190fd5b005b600080fd5b34801561023657600080fd5b5061025d6004803603602081101561024d57600080fd5b50356001600160a01b0316610f27565b60408051918252519081900360200190f35b34801561027b57600080fd5b506102a86004803603604081101561029257600080fd5b506001600160a01b038135169060200135610f39565b604080516001600160a01b03998a168152602081019890985295909716868601526060860193909352901515608085015260a0840152151560c083015260e08201929092529051908190036101000190f35b34801561030657600080fd5b506102236004803603606081101561031d57600080fd5b506001600160a01b0381358116916020810135916040909101351661100c565b34801561034957600080fd5b506102236004803603602081101561036057600080fd5b5035611417565b34801561037357600080fd5b506102236004803603606081101561038a57600080fd5b506001600160a01b038135811691602081013591604090910135166114a8565b3480156103b657600080fd5b506103bf611857565b604080516001600160a01b039092168252519081900360200190f35b61022360048036036101008110156103f257600080fd5b810190602081018135600160201b81111561040c57600080fd5b82018360208201111561041e57600080fd5b803590602001918460208302840111600160201b8311171561043f57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602081013590604081013590606081013515159060808101359060a081013515159060c001351515611866565b3480156104b257600080fd5b506104df600480360360408110156104c957600080fd5b506001600160a01b038135169060200135611bef565b604080516001600160a01b039b8c168152998b1660208b015289810198909852958916606089015293909716608087015260a0860191909152151560c085015260e08401949094529215156101008301526101208201929092529051908190036101400190f35b34801561055257600080fd5b5061055b611c61565b604080519115158252519081900360200190f35b34801561057b57600080fd5b506103bf611c6a565b34801561059057600080fd5b50610223600480360360208110156105a757600080fd5b5035611c79565b3480156105ba57600080fd5b5061025d611f83565b3480156105cf57600080fd5b50610614600480360360a08110156105e657600080fd5b5080351515906020810135906001600160a01b03604082013516906060810135151590608001351515611f8f565b6040805192835290151560208301528051918290030190f35b34801561063957600080fd5b5061025d6120f8565b34801561064e57600080fd5b5061067b6004803603604081101561066557600080fd5b506001600160a01b0381351690602001356120fe565b604080516001600160a01b039098168852602088019690965286860194909452606086019290925215156080850152151560a084015260c0830152519081900360e00190f35b3480156106cd57600080fd5b50610223600480360360608110156106e457600080fd5b810190602081018135600160201b8111156106fe57600080fd5b82018360208201111561071057600080fd5b803590602001918460208302840111600160201b8311171561073157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561078057600080fd5b82018360208201111561079257600080fd5b803590602001918460208302840111600160201b831117156107b357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561080257600080fd5b82018360208201111561081457600080fd5b803590602001918460208302840111600160201b8311171561083557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612152945050505050565b34801561087f57600080fd5b5061025d6121ea565b34801561089457600080fd5b5061025d6121f0565b3480156108a957600080fd5b50610223600480360360808110156108c057600080fd5b50803590602081013590604081013590606001351515612200565b3480156108e757600080fd5b5061025d600480360360208110156108fe57600080fd5b50356001600160a01b0316612358565b34801561091a57600080fd5b506102236004803603602081101561093157600080fd5b5035612502565b34801561094457600080fd5b50610223600480360360a081101561095b57600080fd5b50803590602081013590604081013590606081013590608001351515612774565b34801561098857600080fd5b5061025d6004803603602081101561099f57600080fd5b50356001600160a01b03166128dc565b61022360048036036101608110156109c657600080fd5b810190602081018135600160201b8111156109e057600080fd5b8201836020820111156109f257600080fd5b803590602001918460208302840111600160201b83111715610a1357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505082359350506001600160a01b036020830135811692604081013592506060810135916080820135169060a081013515159060c08101359060e081013515159061010081013590610120013515156128ee565b610223600480360360e0811015610aaf57600080fd5b506001600160a01b03813581169160208101359160408201351690606081013590608081013515159060a08101359060c001351515612cf0565b348015610af557600080fd5b5061055b60048036036040811015610b0c57600080fd5b810190602081018135600160201b811115610b2657600080fd5b820183602082011115610b3857600080fd5b803590602001918460208302840111600160201b83111715610b5957600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250612da1915050565b348015610ba557600080fd5b5061022360048036036080811015610bbc57600080fd5b50803590602081013590604081013590606001351515612fb8565b348015610be357600080fd5b5061022360048036036020811015610bfa57600080fd5b50356001600160a01b0316613181565b348015610c1657600080fd5b50610c4360048036036040811015610c2d57600080fd5b506001600160a01b03813516906020013561322b565b604080516001600160a01b039a8b168152988a1660208a015296909816878701526060870194909452608086019290925260a0850152151560c0840152151560e08301526101008201929092529051908190036101200190f35b348015610ca957600080fd5b5061022360048036036060811015610cc057600080fd5b506001600160a01b038135811691602081013591604090910135166133e8565b348015610cec57600080fd5b50610d1960048036036040811015610d0357600080fd5b506001600160a01b038135169060200135613851565b604080516001600160a01b039a8b1681526020810199909952968916888801529490971660608701526080860192909252151560a085015260c084015292151560e08301526101008201929092529051908190036101200190f35b348015610d8057600080fd5b5061025d60048036036020811015610d9757600080fd5b50356001600160a01b0316613937565b348015610db357600080fd5b50610223600480360360c0811015610dca57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060808101359060a00135613949565b348015610e0c57600080fd5b50610e3960048036036040811015610e2357600080fd5b506001600160a01b038135169060200135613ab8565b604080516001600160a01b039a8b168152988a1660208a0152888101979097529490971660608701526080860192909252151560a085015260c084015292151560e08301526101008201929092529051908190036101200190f35b348015610ea057600080fd5b506103bf613b22565b348015610eb557600080fd5b5061022360048036036020811015610ecc57600080fd5b5035613b31565b348015610edf57600080fd5b506103bf613e6e565b348015610ef457600080fd5b506103bf613e7d565b348015610f0957600080fd5b5061022360048036036020811015610f2057600080fd5b5035613e8c565b60066020526000908152604090205481565b600080600080600080600080610f4d614fd0565b505050506001600160a01b03968716600090815260036020818152604080842099845298815291889020885161012081018a5281548b16815260018201548b1693810184905260028201549981018a9052918101549099166060820181905260048a01546080830181905260058b015460ff908116151560a0850181905260068d015460c0860181905260078e0154909216151560e086018190526008909d0154610100909501859052949c9a9b929a91995093975092955093509150565b60026000541415611052576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b600260005561105f61501c565b6001600160a01b038085166000908152600560209081526040808320878452825291829020825161010081018452815490941684526001810180548451818502810185019095528085529193858401939092908301828280156110eb57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116110cd575b5050509183525050600282015460208201526003820154604082015260048201546060820152600582015460ff8082161515608084015261010090910416151560a082015260069091015460c09091015280519091506001600160a01b0316611189576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b8060a00151156111e0576111a581602001518260800151612da1565b6111e05760405162461bcd60e51b81526004018080602001828103825260268152602001806152f16026913960400191505060405180910390fd5b6001600160a01b0384166000908152600560209081526040808320868452909152812080546001600160a01b03191681559061121f600183018261506e565b50600060028201819055600382018190556004820181905560058201805461ffff191690556006909101819055600b54604083015160208401518051611292946001600160a01b03909416939061127257fe5b60200260200101516001600160a01b0316613f1d9092919063ffffffff16565b600854602082015180516000926001600160a01b0316919060001981019081106112b857fe5b60200260200101516001600160a01b03161480156112d757508160c001515b15611304576112ef8260200151836060015130613f74565b90506112ff8183600001516140a3565b61131e565b61131b826020015183606001518460000151613f74565b90505b61132c8260e00151846140a3565b846001600160a01b03167f7e1fe496989eea92b738a562dbf9c0ae6aa6fcf3f1ef09e95ee4f7603721706b858460200151856040015186606001518688608001518960a001518a60c001518b60e00151604051808a8152602001806020018981526020018881526020018781526020018681526020018515158152602001841515815260200183815260200182810382528a818151815260200191508051906020019060200280838360005b838110156113f05781810151838201526020016113d8565b505050509050019a505050505050505050505060405180910390a250506001600055505050565b6007546001600160a01b0316331461146d576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600d8190556040805182815290517fe46d9daf6d25f7615efa1d0183b90ac6759d85014b598e409aadf0fd918d59a69181900360200190a150565b600260005414156114ee576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000556114fb614fd0565b506001600160a01b03808416600090815260036020818152604080842087855282529283902083516101208101855281548616808252600183015487169382019390935260028201549481019490945291820154909316606083015260048101546080830152600581015460ff908116151560a0840152600682015460c0840152600782015416151560e083015260080154610100820152906115d3576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b60006115f48260e001518360c0015184606001518560a00151156001611f8f565b506001600160a01b0380871660009081526003602081815260408084208a8552825280842080546001600160a01b0319908116825560018201805482169055600282018690559381018054909416909355600480840185905560058401805460ff199081169091556006850186905560078501805490911690556008909301849055600a5488518984015160608b01518b85015160808d015160a08e01518751632662166b60e01b8152958c1699860199909952928a16602485015290891660448401526064830152608482015293151560a48501523060c4850152905195965092949290931692632662166b9260e48084019382900301818787803b1580156116fd57600080fd5b505af1158015611711573d6000803e3d6000fd5b505050506040513d602081101561172757600080fd5b505160085460208501519192506001600160a01b039182169116141561175a576117558184600001516140a3565b611777565b82516020840151611777916001600160a01b039091169083613f1d565b611786836101000151856140a3565b82600001516001600160a01b03167f9a382661d6573da86db000471303be6f0b2b1bb66089b08e3c16a85d7b6e94f88685602001518660400151876060015188608001518960a001518a60c001518b60e001518c61010001518c604051808b81526020018a6001600160a01b03168152602001898152602001886001600160a01b03168152602001878152602001861515815260200185815260200184151581526020018381526020018281526020019a505050505050505050505060405180910390a25050600160005550505050565b6007546001600160a01b031681565b600260005414156118ac576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000819055885114806118c2575087516003145b611901576040805162461bcd60e51b815260206004820152601f6024820152600080516020615317833981519152604482015290519081900360640190fd5b8760018951038151811061191157fe5b60200260200101516001600160a01b03168860008151811061192f57fe5b60200260200101516001600160a01b0316141561198e576040805162461bcd60e51b815260206004820152601860248201527709ee4c8cae484deded67440d2dcecc2d8d2c840bee0c2e8d60431b604482015290519081900360640190fd5b600087116119e2576040805162461bcd60e51b815260206004820152601c60248201527b27b93232b92137b7b59d1034b73b30b634b2102fb0b6b7bab73a24b760211b604482015290519081900360640190fd5b600c54831015611a235760405162461bcd60e51b81526004018080602001828103825260258152602001806152856025913960400191505060405180910390fd5b611a2b614120565b8115611ae65760085488516001600160a01b03909116908990600090611a4d57fe5b60200260200101516001600160a01b031614611a9a5760405162461bcd60e51b81526004018080602001828103825260258152602001806152606025913960400191505060405180910390fd5b611aa48388614192565b3414611ae15760405162461bcd60e51b81526004018080602001828103825260268152602001806152cb6026913960400191505060405180910390fd5b611bd0565b823414611b245760405162461bcd60e51b815260040180806020018281038252602e815260200180615361602e913960400191505060405180910390fd5b600a5488516001600160a01b0390911690631b827878908a90600090611b4657fe5b602002602001015133308b6040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b03168152602001828152602001945050505050600060405180830381600087803b158015611bb757600080fd5b505af1158015611bcb573d6000803e3d6000fd5b505050505b611be0338989898989878a6141ea565b50506001600055505050505050565b6001602081815260009384526040808520909152918352912080549181015460028201546003830154600484015460058501546006860154600787015460088801546009909801546001600160a01b03998a1699978816989697958616969490951694929360ff92831693919216908a565b600e5460ff1681565b6008546001600160a01b031681565b60026000541415611cbf576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055611ccc61508f565b5033600090815260016020818152604080842085855282529283902083516101408101855281546001600160a01b039081168083529483015481169382019390935260028201549481019490945260038101548216606085015260048101549091166080840152600581015460a0840152600681015460ff908116151560c0850152600782015460e0850152600882015416151561010084015260090154610120830152611daf576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b3360009081526001602081815260408084208685528252832080546001600160a01b0319908116825592810180548416905560028101849055600381018054841690556004810180549093169092556005820183905560068201805460ff19908116909155600783018490556008808401805490921690915560099092019290925554908201516001600160a01b0390811691161415611e7357611e6e611e68826040015183610120015161419290919063ffffffff16565b336140a3565b611ea8565b611e9933826040015183602001516001600160a01b0316613f1d9092919063ffffffff16565b611ea8816101200151336140a3565b80600001516001600160a01b03167fd500f34e0ec655b7614ae42e1d9c666d5e4dde909a1297829f8c5ecf00805d328383602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b6101200151604051808b81526020018a6001600160a01b03168152602001898152602001886001600160a01b03168152602001876001600160a01b03168152602001868152602001851515815260200184815260200183151581526020018281526020019a505050505050505050505060405180910390a250506001600055565b670de0b6b3a764000081565b60008060008461201757600b54604080516340d3096b60e11b81526001600160a01b038981166004830152915191909216916381a612d6916024808301926020929190829003018186803b158015611fe657600080fd5b505afa158015611ffa573d6000803e3d6000fd5b505050506040513d602081101561201057600080fd5b5051612091565b600b5460408051637092736960e11b81526001600160a01b0389811660048301529151919092169163e124e6d2916024808301926020929190829003018186803b15801561206457600080fd5b505afa158015612078573d6000803e3d6000fd5b505050506040513d602081101561208e57600080fd5b50515b90506000886120a2578782106120a6565b8782115b905084156120ea57806120ea5760405162461bcd60e51b81526004018080602001828103825260268152602001806152f16026913960400191505060405180910390fd5b909890975095505050505050565b600c5481565b600560208181526000938452604080852090915291835291208054600282015460038301546004840154948401546006909401546001600160a01b03909316949193909260ff808316926101009004169087565b60005b83518110156121825761217a84828151811061216d57fe5b6020026020010151613b31565b600101612155565b5060005b82518110156121b3576121ab83828151811061219e57fe5b6020026020010151611c79565b600101612186565b5060005b81518110156121e4576121dc8282815181106121cf57fe5b6020026020010151612502565b6001016121b7565b50505050565b600d5481565b68327cb2734119d3b7a9601e1b81565b60026000541415612246576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000908155338152600160209081526040808320878452909152902080546001600160a01b03166122ae576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b6007810183905560088101805483151560ff19909116811790915560058201859055600382015460048301546006840154604080518a81526001600160a01b039485166020820152929093168284015260ff16151560608201526080810187905260a0810186905260c08101929092525133917f0a0360dd5c354235bbf8d386ba3b24ef8134088e0785677de1504df219d9149a919081900360e00190a250506001600055505050565b600b5460408051632c668ec160e01b81526001600160a01b038481166004830152670de0b6b3a76400006024830152915160009384931691632c668ec1916044808301926020929190829003018186803b1580156123b557600080fd5b505afa1580156123c9573d6000803e3d6000fd5b505050506040513d60208110156123df57600080fd5b5051600b54604080516340d3096b60e11b81526001600160a01b038781166004830152915193945060009391909216916381a612d6916024808301926020929190829003018186803b15801561243457600080fd5b505afa158015612448573d6000803e3d6000fd5b505050506040513d602081101561245e57600080fd5b5051600b54604080516323b95ceb60e21b81526001600160a01b03888116600483015291519394506000939190921691638ee573ac916024808301926020929190829003018186803b1580156124b357600080fd5b505afa1580156124c7573d6000803e3d6000fd5b505050506040513d60208110156124dd57600080fd5b505190506124f9600a82900a6124f385856143f9565b90614452565b95945050505050565b60026000541415612548576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055612555614fd0565b5033600090815260036020818152604080842085855282529283902083516101208101855281546001600160a01b03908116808352600184015482169483019490945260028301549582019590955292810154909316606083015260048301546080830152600583015460ff908116151560a0840152600684015460c0840152600784015416151560e08301526008909201546101008201529061262e576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b336000818152600360208181526040808420878552909152822080546001600160a01b03199081168255600182018054821690556002820184905591810180549092169091556004810182905560058101805460ff19908116909155600682018390556007820180549091169055600801556101008201516126af916140a3565b80600001516001600160a01b03167f1154174c82984656b028c8021671988f60a346497e56fe02554761184f82a0758383602001518460400151856060015186608001518760a001518860c001518960e001518a6101000151604051808a8152602001896001600160a01b03168152602001888152602001876001600160a01b0316815260200186815260200185151581526020018481526020018315158152602001828152602001995050505050505050505060405180910390a250506001600055565b600260005414156127ba576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000908155338152600360209081526040808320888452909152902080546001600160a01b0316612822576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b6006810183905560078101805483151560ff1990911681179091556004820185905560028201869055600182015460038301546005840154604080518b81526001600160a01b0394851660208201528082018b90529290931660608301526080820188905260ff16151560a082015260c0810186905260e08101929092525133917f75781255bc71c83f89f29e5a2599f2c174a562d2cd8f2e818a47f132e728049891908190036101000190a25050600160005550505050565b60026020526000908152604090205481565b60026000541415612934576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055612941614120565b600c548210156129825760405162461bcd60e51b81526004018080602001828103825260258152602001806152856025913960400191505060405180910390fd5b8015612a3d576008548b516001600160a01b03909116908c906000906129a457fe5b60200260200101516001600160a01b0316146129f15760405162461bcd60e51b81526004018080602001828103825260258152602001806152606025913960400191505060405180910390fd5b6129fb828b614192565b3414612a385760405162461bcd60e51b81526004018080602001828103825260268152602001806152cb6026913960400191505060405180910390fd5b612b27565b813414612a7b5760405162461bcd60e51b815260040180806020018281038252602e815260200180615361602e913960400191505060405180910390fd5b600a548b516001600160a01b0390911690631b827878908d90600090612a9d57fe5b602002602001015133308e6040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b03168152602001828152602001945050505050600060405180830381600087803b158015612b0e57600080fd5b505af1158015612b22573d6000803e3d6000fd5b505050505b60008b60018d510381518110612b3957fe5b60200260200101519050600060018d511115612bfe57816001600160a01b03168d600081518110612b6657fe5b60200260200101516001600160a01b03161415612bc5576040805162461bcd60e51b815260206004820152601860248201527709ee4c8cae484deded67440d2dcecc2d8d2c840bee0c2e8d60431b604482015290519081900360640190fd5b612bec600b60009054906101000a90046001600160a01b03168d8f60008151811061127257fe5b612bf78d8b30613f74565b9050612c01565b508a5b600b5460408051630a48d5a960e01b81526001600160a01b0385811660048301526024820185905291516000939290921691630a48d5a991604480820192602092909190829003018186803b158015612c5957600080fd5b505afa158015612c6d573d6000803e3d6000fd5b505050506040513d6020811015612c8357600080fd5b5051600d54909150811015612cc95760405162461bcd60e51b81526004018080602001828103825260228152602001806151bd6022913960400191505060405180910390fd5b50612cdc3383838b8f8e8d8d8d8d614491565b505060016000555050505050505050505050565b60026000541415612d36576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055612d43614120565b600c543411612d835760405162461bcd60e51b81526004018080602001828103825260258152602001806152856025913960400191505060405180910390fd5b612d933386868a8a888888614764565b505060016000555050505050565b6000825160021480612db4575082516003145b612df3576040805162461bcd60e51b815260206004820152601f6024820152600080516020615317833981519152604482015290519081900360640190fd5b600083600081518110612e0257fe5b60200260200101519050600084600186510381518110612e1e57fe5b602090810291909101015160095490915060009081906001600160a01b0385811691161415612e6b57612e6487600181518110612e5757fe5b6020026020010151612358565b9150612ee7565b600b54604080516340d3096b60e11b81526001600160a01b038781166004830152915191909216916381a612d6916024808301926020929190829003018186803b158015612eb857600080fd5b505afa158015612ecc573d6000803e3d6000fd5b505050506040513d6020811015612ee257600080fd5b505191505b6009546001600160a01b0384811691161415612f10575068327cb2734119d3b7a9601e1b612f8c565b600b5460408051637092736960e11b81526001600160a01b0386811660048301529151919092169163e124e6d2916024808301926020929190829003018186803b158015612f5d57600080fd5b505afa158015612f71573d6000803e3d6000fd5b505050506040513d6020811015612f8757600080fd5b505190505b6000612fa8836124f38468327cb2734119d3b7a9601e1b6143f9565b8710955050505050505b92915050565b60026000541415612ffe576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000908155338152600560209081526040808320878452909152902080546001600160a01b0316613066576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b838160030181905550828160040181905550818160050160006101000a81548160ff021916908315150217905550336001600160a01b03167fa7f9f4a25eb76f5ec01b1a429d95d6a00833f0f137c88827c58799a1c1ff0dfe868360010184600201548888888860050160019054906101000a900460ff168960060154604051808981526020018060200188815260200187815260200186815260200185151581526020018415158152602001838152602001828103825289818154815260200191508054801561316057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613142575b5050995050505050505050505060405180910390a250506001600055505050565b6007546001600160a01b031633146131d7576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600780546001600160a01b0383166001600160a01b0319909116811790915560408051918252517fe24c39186e9137521953beaa8446e71f55b8f12296984f9d4273ceb1af728d909181900360200190a150565b600080600080600080600080600061324161501c565b6001600160a01b03808d1660009081526005602090815260408083208f8452825291829020825161010081018452815490941684526001810180548451818502810185019095528085529193858401939092908301828280156132cd57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116132af575b505050918352505060028201546020808301919091526003830154604083015260048301546060830152600583015460ff8082161515608085015261010090910416151560a083015260069092015460c0909101528101515190915061333457600061334e565b806020015160008151811061334557fe5b60200260200101515b60018260200151511161336257600061337c565b816020015160018151811061337357fe5b60200260200101515b6002836020015151116133905760006133aa565b82602001516002815181106133a157fe5b60200260200101515b8360400151846060015185608001518660a001518760c001518860e00151995099509950995099509950995099509950509295985092959850929598565b6002600054141561342e576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b600260005561343b61508f565b506001600160a01b038084166000908152600160208181526040808420878552825292839020835161014081018552815486168082529382015486169281019290925260028101549382019390935260038301548416606082015260048301549093166080840152600582015460a0840152600682015460ff908116151560c0850152600783015460e08501526008830154161515610100840152600990910154610120830152613521576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b60006135428261010001518360e0015184608001518560c001516001611f8f565b506001600160a01b0380871660009081526001602081815260408084208a8552825280842080546001600160a01b0319908116825593810180548516905560028101859055600381018054851690556004810180549094169093556005830184905560068301805460ff19908116909155600784018590556008840180549091169055600990920192909255600b5490860151918601519394506135ea938316921690613f1d565b81606001516001600160a01b031682602001516001600160a01b0316146136c257604080516002808252606080830184529260208301908036833701905050905082602001518160008151811061363d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505082606001518160018151811061366f57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600061369d82600030613f74565b600b5460608601519192506136bf916001600160a01b03908116911683613f1d565b50505b600a5482516060840151608085015160a086015160c087015160408051630f8ee8bb60e11b81526001600160a01b03968716600482015294861660248601529285166044850152606484019190915215156084830152519190921691631f1dd1769160a480830192600092919082900301818387803b15801561374457600080fd5b505af1158015613758573d6000803e3d6000fd5b5050505061376b826101200151846140a3565b81600001516001600160a01b03167f7fb1c74d1ea6aa1c9c585e17ce8274c8ff98745e85e7459b73f87d784494f58e8584602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001518c61012001518c604051808c81526020018b6001600160a01b031681526020018a8152602001896001600160a01b03168152602001886001600160a01b03168152602001878152602001861515815260200185815260200184151581526020018381526020018281526020019b50505050505050505050505060405180910390a250506001600055505050565b600080600080600080600080600061386761508f565b505050506001600160a01b0397881660009081526001602081815260408084209a845299815291899020895161014081018b5281548c168152918101548b1692820183905260028101549982018a905260038101548b16606083018190526004820154909b1660808301819052600582015460a08401819052600683015460ff908116151560c08601819052600785015460e08701819052600886015490921615156101008701819052600990950154610120909601869052959e9c9d9c929b5090995093975092955093509150565b60046020526000908152604090205481565b6007546001600160a01b0316331461399f576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600e5460ff16156139f7576040805162461bcd60e51b815260206004820152601e60248201527f4f72646572426f6f6b3a20616c726561647920696e697469616c697a65640000604482015290519081900360640190fd5b600e805460ff19166001179055600a80546001600160a01b038089166001600160a01b03199283168117909355600b8054898316908416811790915560088054898416908516811790915560098054938916939094168317909355600c869055600d8590556040805194855260208501919091528381019290925260608301526080820184905260a08201839052517fcfb7ef8749fafc8da2af1ba3d025479ffc4e58f7dc420113e112512a3bda59639181900360c00190a1505050505050565b600360208181526000938452604080852090915291835291208054600182015460028301549383015460048401546005850154600686015460078701546008909701546001600160a01b039687169895871697959690941694929360ff9283169391929091169089565b6009546001600160a01b031681565b60026000541415613b77576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055613b8461501c565b33600090815260056020908152604080832085845282529182902082516101008101845281546001600160a01b0316815260018201805485518186028101860190965280865291949293858101939290830182828015613c0d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613bef575b5050509183525050600282015460208201526003820154604082015260048201546060820152600582015460ff8082161515608084015261010090910416151560a082015260069091015460c09091015280519091506001600160a01b0316613cab576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b336000908152600560209081526040808320858452909152812080546001600160a01b031916815590613ce1600183018261506e565b50600060028201819055600382018190556004820181905560058201805461ffff191690556006909101819055600854602083015180516001600160a01b03909216929091613d2c57fe5b60200260200101516001600160a01b03161415613d6657613d61611e6882604001518360e0015161419290919063ffffffff16565b613d8e565b613d80338260400151836020015160008151811061127257fe5b613d8e8160e00151336140a3565b336001600160a01b03167fefd66d4f9c2f880c70aedeb5b26a44fb474cea07e5d6c533f2d27c303d5d94538383602001518460400151856060015186608001518760a001518860c001518960e00151604051808981526020018060200188815260200187815260200186815260200185151581526020018415158152602001838152602001828103825289818151815260200191508051906020019060200280838360005b83811015613e4b578181015183820152602001613e33565b50505050905001995050505050505050505060405180910390a250506001600055565b600a546001600160a01b031681565b600b546001600160a01b031681565b6007546001600160a01b03163314613ee2576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600c8190556040805182815290517fbde5eafdc37b81830d70124cddccaaa6d034e71dda3c8fc18a959ca76a7cbcfc9181900360200190a150565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613f6f9084906149da565b505050565b6000835160021415613fbb57613fb484600081518110613f9057fe5b602002602001015185600181518110613fa557fe5b60200260200101518585614a8b565b905061409c565b835160031415614061576000613ffc85600081518110613fd757fe5b602002602001015186600181518110613fec57fe5b6020026020010151600030614a8b565b9050614025600b60009054906101000a90046001600160a01b0316828760018151811061127257fe5b6140598560018151811061403557fe5b60200260200101518660028151811061404a57fe5b60200260200101518686614a8b565b91505061409c565b6040805162461bcd60e51b815260206004820152601f6024820152600080516020615317833981519152604482015290519081900360640190fd5b9392505050565b60085460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921691632e1a7d4d9160248082019260009290919082900301818387803b1580156140f057600080fd5b505af1158015614104573d6000803e3d6000fd5b5061411c925050506001600160a01b03821683614c6a565b5050565b341561419057600860009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561417657600080fd5b505af115801561418a573d6000803e3d6000fd5b50505050505b565b60008282018381101561409c576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6001600160a01b03881660009081526006602052604090205461420b61501c565b6040518061010001604052808b6001600160a01b031681526020018a81526020018981526020018881526020018781526020018615158152602001851515815260200184815250905061426860018361419290919063ffffffff16565b6001600160a01b038b8116600090815260066020908152604080832094909455600581528382208683528152929020835181546001600160a01b031916921691909117815582820151805184936142c69260018501929101906150e3565b5060408201518160020155606082015181600301556080820151816004015560a08201518160050160006101000a81548160ff02191690831515021790555060c08201518160050160016101000a81548160ff02191690831515021790555060e08201518160060155905050896001600160a01b03167fdf06bb56ffc4029dc0b62b68bb5bbadea93a38b530cefc9b81afb742a6555d88838b8b8b8b8b8b8b604051808981526020018060200188815260200187815260200186815260200185151581526020018415158152602001838152602001828103825289818151815260200191508051906020019060200280838360005b838110156143d35781810151838201526020016143bb565b50505050905001995050505050505050505060405180910390a250505050505050505050565b60008261440857506000612fb2565b8282028284828161441557fe5b041461409c5760405162461bcd60e51b81526004018080602001828103825260218152602001806152aa6021913960400191505060405180910390fd5b600061409c83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250614d4f565b336000908152600260205260409020546144a961508f565b6040518061014001604052808d6001600160a01b031681526020018c6001600160a01b031681526020018b81526020018a6001600160a01b03168152602001896001600160a01b031681526020018881526020018715158152602001868152602001851515815260200184815250905061452d60018361419290919063ffffffff16565b600260008e6001600160a01b03166001600160a01b031681526020019081526020016000208190555080600160008e6001600160a01b03166001600160a01b03168152602001908152602001600020600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816002015560608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a0820151816005015560c08201518160060160006101000a81548160ff02191690831515021790555060e082015181600701556101008201518160080160006101000a81548160ff02191690831515021790555061012082015181600901559050508b6001600160a01b03167fb27b9afe3043b93788c40cfc3cc73f5d928a2e40f3ba01820b246426de8fa1b9838d8d8d8d8d8d8d8d8d604051808b81526020018a6001600160a01b03168152602001898152602001886001600160a01b03168152602001876001600160a01b03168152602001868152602001851515815260200184815260200183151581526020018281526020019a505050505050505050505060405180910390a2505050505050505050505050565b6001600160a01b038816600090815260046020526040902054614785614fd0565b5060408051610120810182526001600160a01b03808c1682528a8116602083015291810189905290871660608201526080810186905284151560a082015260c0810184905282151560e0820152346101008201526147e4826001614192565b600460008c6001600160a01b03166001600160a01b031681526020019081526020016000208190555080600360008c6001600160a01b03166001600160a01b03168152602001908152602001600020600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816002015560608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506080820151816004015560a08201518160050160006101000a81548160ff02191690831515021790555060c0820151816006015560e08201518160070160006101000a81548160ff0219169083151502179055506101008201518160080155905050896001600160a01b03167f48ee333d2a65cc45fdb83bc012920d89181c3377390cd239d2b63f2bef67a02d838b8b8b8b8b8b8b34604051808a8152602001896001600160a01b03168152602001888152602001876001600160a01b0316815260200186815260200185151581526020018481526020018315158152602001828152602001995050505050505050505060405180910390a250505050505050505050565b6060614a2f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614df19092919063ffffffff16565b805190915015613f6f57808060200190516020811015614a4e57600080fd5b5051613f6f5760405162461bcd60e51b815260040180806020018281038252602a815260200180615337602a913960400191505060405180910390fd5b60095460009081906001600160a01b0386811691161415614b3057600b546040805163817bb85760e01b81526001600160a01b03898116600483015286811660248301529151919092169163817bb8579160448083019260209291908290030181600087803b158015614afd57600080fd5b505af1158015614b11573d6000803e3d6000fd5b505050506040513d6020811015614b2757600080fd5b50519050614c2b565b6009546001600160a01b0387811691161415614b9d57600b5460408051630711e61960e41b81526001600160a01b03888116600483015286811660248301529151919092169163711e61909160448083019260209291908290030181600087803b158015614afd57600080fd5b600b5460408051634998b10960e11b81526001600160a01b038981166004830152888116602483015286811660448301529151919092169163933162129160648083019260209291908290030181600087803b158015614bfc57600080fd5b505af1158015614c10573d6000803e3d6000fd5b505050506040513d6020811015614c2657600080fd5b505190505b838110156124f95760405162461bcd60e51b815260040180806020018281038252602181526020018061523f6021913960400191505060405180910390fd5b80471015614cbf576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015290519081900360640190fd5b6040516000906001600160a01b0384169083908381818185875af1925050503d8060008114614d0a576040519150601f19603f3d011682016040523d82523d6000602084013e614d0f565b606091505b5050905080613f6f5760405162461bcd60e51b815260040180806020018281038252603a8152602001806151df603a913960400191505060405180910390fd5b60008183614ddb5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614da0578181015183820152602001614d88565b50505050905090810190601f168015614dcd5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581614de757fe5b0495945050505050565b6060614e008484600085614e08565b949350505050565b606082471015614e495760405162461bcd60e51b81526004018080602001828103825260268152602001806152196026913960400191505060405180910390fd5b614e5285614f64565b614ea3576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310614ee25780518252601f199092019160209182019101614ec3565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614f44576040519150601f19603f3d011682016040523d82523d6000602084013e614f49565b606091505b5091509150614f59828286614f6a565b979650505050505050565b3b151590565b60608315614f7957508161409c565b825115614f895782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315614da0578181015183820152602001614d88565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b60405180610100016040528060006001600160a01b0316815260200160608152602001600081526020016000815260200160008152602001600015158152602001600015158152602001600081525090565b508054600082559060005260206000209081019061508c9190615148565b50565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081019190915290565b828054828255906000526020600020908101928215615138579160200282015b8281111561513857825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615103565b5061514492915061515d565b5090565b5b808211156151445760008155600101615149565b5b808211156151445780546001600160a01b031916815560010161515e56fe4f72646572426f6f6b3a206e6f6e2d6578697374656e74206f726465720000005265656e7472616e637947756172643a207265656e7472616e742063616c6c004f72646572426f6f6b3a20696e73756666696369656e7420636f6c6c61746572616c416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d61792068617665207265766572746564416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c4f72646572426f6f6b3a20696e73756666696369656e7420616d6f756e744f75744f72646572426f6f6b3a206f6e6c79207765746820636f756c6420626520777261707065644f72646572426f6f6b3a20696e73756666696369656e7420657865637574696f6e20666565536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f72646572426f6f6b3a20696e636f72726563742076616c7565207472616e736665727265644f72646572426f6f6b3a20696e76616c696420707269636520666f7220657865637574696f6e4f72646572426f6f6b3a20696e76616c6964205f706174682e6c656e677468005361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565644f72646572426f6f6b3a20696e636f727265637420657865637574696f6e20666565207472616e73666572726564a2646970667358221220e85a1f264782d8faab5f0f3dab3cf5b8844d298c594ad34ab45699fcaa9855e464736f6c634300060c0033 \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/OrderBookReader.abi b/src/Managing.Tools.ABI/Gmx/core/OrderBookReader.abi new file mode 100644 index 0000000..e2ffcc2 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/OrderBookReader.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address payable","name":"_orderBookAddress","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256[]","name":"_indices","type":"uint256[]"}],"name":"getDecreaseOrders","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_orderBookAddress","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256[]","name":"_indices","type":"uint256[]"}],"name":"getIncreaseOrders","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_orderBookAddress","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256[]","name":"_indices","type":"uint256[]"}],"name":"getSwapOrders","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/OrderBookReader.bin b/src/Managing.Tools.ABI/Gmx/core/OrderBookReader.bin new file mode 100644 index 0000000..7392c14 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/OrderBookReader.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610e27806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80630ce933b9146100465780632e18146914610199578063c38ccd5014610253575b600080fd5b6101006004803603606081101561005c57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561008f57600080fd5b8201836020820111156100a157600080fd5b803590602001918460208302840111600160201b831117156100c257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061030d945050505050565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561014457818101518382015260200161012c565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561018357818101518382015260200161016b565b5050505090500194505050505060405180910390f35b610100600480360360608110156101af57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b8111156101e257600080fd5b8201836020820111156101f457600080fd5b803590602001918460208302840111600160201b8311171561021557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610675945050505050565b6101006004803603606081101561026957600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561029c57600080fd5b8201836020820111156102ae57600080fd5b803590602001918460208302840111600160201b831117156102cf57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610a17945050505050565b606080610318610db9565b6040518060a001604052806000815260200160008152602001866001600160a01b031681526020016005815260200160028152509050606084518260600151026001600160401b038111801561036d57600080fd5b50604051908082528060200260200182016040528015610397578160200160208202803683370190505b509050606085518360800151026001600160401b03811180156103b957600080fd5b506040519080825280602002602001820160405280156103e3578160200160208202803683370190505b509050875b865184511015610667578684600001518151811061040257fe5b60200260200101518460200181815250506000806000806000806000876001600160a01b031663026032ee8c604001518d602001516040518363ffffffff1660e01b815260040180836001600160a01b03168152602001828152602001925050506101006040518083038186803b15801561047c57600080fd5b505afa158015610490573d6000803e3d6000fd5b505050506040513d6101008110156104a757600080fd5b810190808051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190505050509650965096509650965096509650858a8c606001518d60000151028151811061052457fe5b602002602001018181525050838a8c606001518d60000151026001018151811061054a57fe5b60200260200101818152505082610562576000610565565b60015b60ff168a8c606001518d60000151026002018151811061058157fe5b602002602001018181525050818a8c606001518d6000015102600301815181106105a757fe5b602002602001018181525050806105bf5760006105c2565b60015b60ff168a8c606001518d6000015102600401815181106105de57fe5b60200260200101818152505086898c608001518d60000151028151811061060157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084898c608001518d60000151026001018151811061063b57fe5b6001600160a01b0390921660209283029190910190910152505088516001018952506103e89350505050565b509097909650945050505050565b606080610680610db9565b6040518060a001604052806000815260200160008152602001866001600160a01b031681526020016005815260200160038152509050606084518260600151026001600160401b03811180156106d557600080fd5b506040519080825280602002602001820160405280156106ff578160200160208202803683370190505b509050606085518360800151026001600160401b038111801561072157600080fd5b5060405190808252806020026020018201604052801561074b578160200160208202803683370190505b509050875b865184511015610667578684600001518151811061076a57fe5b6020026020010151846020018181525050600080600080600080600080886001600160a01b031663d0d40cd68d604001518e602001516040518363ffffffff1660e01b815260040180836001600160a01b03168152602001828152602001925050506101206040518083038186803b1580156107e557600080fd5b505afa1580156107f9573d6000803e3d6000fd5b505050506040513d61012081101561081057600080fd5b810190808051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291905050505097509750975097509750975097509750848b8d606001518e60000151028151811061089957fe5b602002602001018181525050838b8d606001518e6000015102600101815181106108bf57fe5b602002602001018181525050828b8d606001518e6000015102600201815181106108e557fe5b602002602001018181525050816108fd576000610900565b60015b60ff168b8d606001518e60000151026003018151811061091c57fe5b60200260200101818152505080610934576000610937565b60015b60ff168b8d606001518e60000151026004018151811061095357fe5b602002602001018181525050878a8d608001518e60000151028151811061097657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050868a8d608001518e6000015102600101815181106109b057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050858a8d608001518e6000015102600201815181106109ea57fe5b6001600160a01b0390921660209283029190910190910152505089516001018a5250610750945050505050565b606080610a22610db9565b6040518060a001604052806000815260200160008152602001866001600160a01b031681526020016005815260200160038152509050606084518260600151026001600160401b0381118015610a7757600080fd5b50604051908082528060200260200182016040528015610aa1578160200160208202803683370190505b509050606085518360800151026001600160401b0381118015610ac357600080fd5b50604051908082528060200260200182016040528015610aed578160200160208202803683370190505b509050875b8651845110156106675786846000015181518110610b0c57fe5b6020026020010151846020018181525050600080600080600080600080886001600160a01b031663d3bab1d18d604001518e602001516040518363ffffffff1660e01b815260040180836001600160a01b03168152602001828152602001925050506101206040518083038186803b158015610b8757600080fd5b505afa158015610b9b573d6000803e3d6000fd5b505050506040513d610120811015610bb257600080fd5b810190808051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291905050505097509750975097509750975097509750868b8d606001518e600001510281518110610c3b57fe5b602002602001018181525050838b8d606001518e600001510260010181518110610c6157fe5b60200260200101818152505082610c79576000610c7c565b60015b60ff168b8d606001518e600001510260020181518110610c9857fe5b602002602001018181525050818b8d606001518e600001510260030181518110610cbe57fe5b60200260200101818152505080610cd6576000610cd9565b60015b60ff168b8d606001518e600001510260040181518110610cf557fe5b602002602001018181525050878a8d608001518e600001510281518110610d1857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050858a8d608001518e600001510260010181518110610d5257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848a8d608001518e600001510260020181518110610d8c57fe5b6001600160a01b0390921660209283029190910190910152505089516001018a5250610af2945050505050565b6040518060a00160405280600081526020016000815260200160006001600160a01b031681526020016000815260200160008152509056fea26469706673582212200490431a3ff46032daa47d7d2efe24198ccd39e8e57ff9d6fccef3b97ebe51b664736f6c634300060c0033 \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/PositionRouter.abi b/src/Managing.Tools.ABI/Gmx/core/PositionRouter.abi new file mode 100644 index 0000000..2c78a5b --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/PositionRouter.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_router","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_shortsTracker","type":"address"},{"internalType":"uint256","name":"_depositFee","type":"uint256"},{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"callbackTarget","type":"address"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"Callback","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockGap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"}],"name":"CancelDecreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockGap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"}],"name":"CancelIncreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"queueIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTime","type":"uint256"}],"name":"CreateDecreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"queueIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasPrice","type":"uint256"}],"name":"CreateIncreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"marginFeeBasisPoints","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"referralCode","type":"bytes32"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"}],"name":"DecreasePositionReferral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockGap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"}],"name":"ExecuteDecreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address[]","name":"path","type":"address[]"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockGap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeGap","type":"uint256"}],"name":"ExecuteIncreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"marginFeeBasisPoints","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"referralCode","type":"bytes32"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"}],"name":"IncreasePositionReferral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"callbackGasLimit","type":"uint256"}],"name":"SetCallbackGasLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minBlockDelayKeeper","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minTimeDelayPublic","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxTimeDelay","type":"uint256"}],"name":"SetDelayValues","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositFee","type":"uint256"}],"name":"SetDepositFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"increasePositionBufferBps","type":"uint256"}],"name":"SetIncreasePositionBufferBps","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isLeverageEnabled","type":"bool"}],"name":"SetIsLeverageEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"longSizes","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"shortSizes","type":"uint256[]"}],"name":"SetMaxGlobalSizes","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minExecutionFee","type":"uint256"}],"name":"SetMinExecutionFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"SetPositionKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"referralStorage","type":"address"}],"name":"SetReferralStorage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"increasePositionRequestKeysStart","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"decreasePositionRequestKeysStart","type":"uint256"}],"name":"SetRequestKeysStartValues","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawFees","type":"event"},{"inputs":[],"name":"BASIS_POINTS_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"callbackGasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"cancelDecreasePosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"cancelIncreasePosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_executionFee","type":"uint256"},{"internalType":"bool","name":"_withdrawETH","type":"bool"},{"internalType":"address","name":"_callbackTarget","type":"address"}],"name":"createDecreasePosition","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"_executionFee","type":"uint256"},{"internalType":"bytes32","name":"_referralCode","type":"bytes32"},{"internalType":"address","name":"_callbackTarget","type":"address"}],"name":"createIncreasePosition","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"_executionFee","type":"uint256"},{"internalType":"bytes32","name":"_referralCode","type":"bytes32"},{"internalType":"address","name":"_callbackTarget","type":"address"}],"name":"createIncreasePositionETH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"decreasePositionRequestKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decreasePositionRequestKeysStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"decreasePositionRequests","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"indexToken","type":"address"},{"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"},{"internalType":"bool","name":"withdrawETH","type":"bool"},{"internalType":"address","name":"callbackTarget","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"decreasePositionsIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executeDecreasePosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_endIndex","type":"uint256"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executeDecreasePositions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executeIncreasePosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_endIndex","type":"uint256"},{"internalType":"address payable","name":"_executionFeeReceiver","type":"address"}],"name":"executeIncreasePositions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feeReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"getDecreasePositionRequestPath","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"getIncreasePositionRequestPath","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getRequestKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getRequestQueueLengths","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"increasePositionBufferBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"increasePositionRequestKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"increasePositionRequestKeysStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"increasePositionRequests","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"indexToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"acceptablePrice","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"blockTime","type":"uint256"},{"internalType":"bool","name":"hasCollateralInETH","type":"bool"},{"internalType":"address","name":"callbackTarget","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"increasePositionsIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isLeverageEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPositionKeeper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxGlobalLongSizes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxGlobalShortSizes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTimeDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBlockDelayKeeper","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minExecutionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTimeDelayPublic","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralStorage","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"sendValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_callbackGasLimit","type":"uint256"}],"name":"setCallbackGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minBlockDelayKeeper","type":"uint256"},{"internalType":"uint256","name":"_minTimeDelayPublic","type":"uint256"},{"internalType":"uint256","name":"_maxTimeDelay","type":"uint256"}],"name":"setDelayValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositFee","type":"uint256"}],"name":"setDepositFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_increasePositionBufferBps","type":"uint256"}],"name":"setIncreasePositionBufferBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isLeverageEnabled","type":"bool"}],"name":"setIsLeverageEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_longSizes","type":"uint256[]"},{"internalType":"uint256[]","name":"_shortSizes","type":"uint256[]"}],"name":"setMaxGlobalSizes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"}],"name":"setMinExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setPositionKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_referralStorage","type":"address"}],"name":"setReferralStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_increasePositionRequestKeysStart","type":"uint256"},{"internalType":"uint256","name":"_decreasePositionRequestKeysStart","type":"uint256"}],"name":"setRequestKeysStartValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shortsTracker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/PositionRouter.bin b/src/Managing.Tools.ABI/Gmx/core/PositionRouter.bin new file mode 100644 index 0000000..2c8756d --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/PositionRouter.bin @@ -0,0 +1 @@ +608060405260646008556011805460ff191660011790553480156200002357600080fd5b50604051620060bb380380620060bb833981810160405260c08110156200004957600080fd5b508051602082015160408301516060840151608085015160a090950151600160008181558154336001600160a01b031991821681179093556003805482166001600160a01b03998a16179055600580548216978916979097179096556006805487169588169590951790945560079690965560048054851695909216949094179055600280549092169093179055600d55615fd0908190620000eb90396000f3fe60806040526004361061027e5760003560e01c80626cc35e146102d357806304225954146103045780630d4d003d146103405780631045c74e1461038d578063126082cf146103c057806312d43a51146103d55780631bca8cf0146103ea5780631ce9cb8f146103ff5780631f28510614610432578063225fc9fd146104d4578063233bfe3b1461050d57806324a084df1461053757806324f746971461057057806327b42c0f14610585578063308aa81f146105be5780633422ead1146105ee57806336eba48a146106295780633a2a80c71461065c5780633e72a262146106715780633fc8cef3146106865780634067b1321461069b5780634278555f146106d1578063490ae210146106fb5780635841fcaa146107255780635b88e8c61461073a5780635d5c22e81461081357806360a362e21461088d57806362f8a3fe146108c6578063633451de146108ff57806363ae210314610932578063657bc5d01461094757806367a527931461095c578063704b6c02146109715780637be7d141146109a45780637c2eb9f714610a8f5780638a54942f14610abb57806395e9bbd714610ae55780639698d25a14610b0f57806398d1e03a14610b425780639a20810014610b575780639b57862014610b90578063ae4d7f9a14610ba5578063cb0269c914610bd8578063cfad57a214610bed578063e1f21c6714610c20578063ef12c67e14610c63578063f255527814610e15578063f2ae372f14610e50578063f2cea6a514610f2f578063f3883d8b14610f6a578063f851a44014610fa3578063f887ea4014610fb8578063fa44457714610fcd578063faf990f314611000578063fbfa77cf14611099578063fc2cee62146110ae576102ce565b366102ce576006546001600160a01b031633146102cc5760405162461bcd60e51b8152600401808060200182810382526023815260200180615f4e6023913960400191505060405180910390fd5b005b600080fd5b3480156102df57600080fd5b506102e86110d8565b604080516001600160a01b039092168252519081900360200190f35b34801561031057600080fd5b5061032e6004803603602081101561032757600080fd5b50356110e7565b60408051918252519081900360200190f35b34801561034c57600080fd5b506103796004803603604081101561036357600080fd5b50803590602001356001600160a01b0316611105565b604080519115158252519081900360200190f35b34801561039957600080fd5b5061032e600480360360208110156103b057600080fd5b50356001600160a01b03166115a3565b3480156103cc57600080fd5b5061032e6115b5565b3480156103e157600080fd5b506102e86115bb565b3480156103f657600080fd5b5061032e6115ca565b34801561040b57600080fd5b5061032e6004803603602081101561042257600080fd5b50356001600160a01b03166115d0565b34801561043e57600080fd5b5061045c6004803603602081101561045557600080fd5b50356115e2565b604080516001600160a01b039e8f1681529c8e1660208e01528c81019b909b5260608c019990995296151560808b0152948a1660a08a015260c089019390935260e088019190915261010087015261012086015261014085015215156101608401529092166101808201529051908190036101a00190f35b3480156104e057600080fd5b50610379600480360360408110156104f757600080fd5b50803590602001356001600160a01b0316611655565b34801561051957600080fd5b506102cc6004803603602081101561053057600080fd5b5035611a1f565b34801561054357600080fd5b506102cc6004803603604081101561055a57600080fd5b506001600160a01b038135169060200135611aa7565b34801561057c57600080fd5b5061032e611b0b565b34801561059157600080fd5b50610379600480360360408110156105a857600080fd5b50803590602001356001600160a01b0316611b11565b3480156105ca57600080fd5b506102cc600480360360408110156105e157600080fd5b5080359060200135611f73565b3480156105fa57600080fd5b506102cc6004803603604081101561061157600080fd5b506001600160a01b0381351690602001351515612009565b34801561063557600080fd5b506103796004803603602081101561064c57600080fd5b50356001600160a01b03166120b6565b34801561066857600080fd5b5061032e6120cb565b34801561067d57600080fd5b506103796120d1565b34801561069257600080fd5b506102e86120da565b3480156106a757600080fd5b506102cc600480360360608110156106be57600080fd5b50803590602081013590604001356120e9565b3480156106dd57600080fd5b5061032e600480360360208110156106f457600080fd5b503561218a565b34801561070757600080fd5b506102cc6004803603602081101561071e57600080fd5b5035612197565b34801561073157600080fd5b5061032e61221f565b61032e600480360361012081101561075157600080fd5b810190602081018135600160201b81111561076b57600080fd5b82018360208201111561077d57600080fd5b803590602001918460208302840111600160201b8311171561079e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b03908116935060208301359260408101359250606081013515159160808201359160a08101359160c08201359160e0013516612225565b34801561081f57600080fd5b5061083d6004803603602081101561083657600080fd5b50356123e1565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610879578181015183820152602001610861565b505050509050019250505060405180910390f35b34801561089957600080fd5b50610379600480360360408110156108b057600080fd5b50803590602001356001600160a01b0316612517565b3480156108d257600080fd5b5061032e600480360360408110156108e957600080fd5b506001600160a01b0381351690602001356128b5565b34801561090b57600080fd5b5061032e6004803603602081101561092257600080fd5b50356001600160a01b03166128fb565b34801561093e57600080fd5b5061032e61290d565b34801561095357600080fd5b506102e8612913565b34801561096857600080fd5b5061032e612922565b34801561097d57600080fd5b506102cc6004803603602081101561099457600080fd5b50356001600160a01b0316612928565b61032e60048036036101608110156109bb57600080fd5b810190602081018135600160201b8111156109d557600080fd5b8201836020820111156109e757600080fd5b803590602001918460208302840111600160201b83111715610a0857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b039081169350602083013592604081013592506060810135151591608082013581169160a08101359160c08201359160e0810135916101008201351515916101200135166129c9565b348015610a9b57600080fd5b506102cc60048036036020811015610ab257600080fd5b50351515612b79565b348015610ac757600080fd5b506102cc60048036036020811015610ade57600080fd5b5035612c0d565b348015610af157600080fd5b5061083d60048036036020811015610b0857600080fd5b5035612c95565b348015610b1b57600080fd5b5061032e60048036036020811015610b3257600080fd5b50356001600160a01b0316612dc3565b348015610b4e57600080fd5b5061032e612dd5565b348015610b6357600080fd5b506102cc60048036036040811015610b7a57600080fd5b50803590602001356001600160a01b0316612ddb565b348015610b9c57600080fd5b5061032e612fb3565b348015610bb157600080fd5b506102cc60048036036020811015610bc857600080fd5b50356001600160a01b0316612fb9565b348015610be457600080fd5b5061032e61305a565b348015610bf957600080fd5b506102cc60048036036020811015610c1057600080fd5b50356001600160a01b0316613060565b348015610c2c57600080fd5b506102cc60048036036060811015610c4357600080fd5b506001600160a01b038135811691602081013590911690604001356130cf565b348015610c6f57600080fd5b506102cc60048036036060811015610c8657600080fd5b810190602081018135600160201b811115610ca057600080fd5b820183602082011115610cb257600080fd5b803590602001918460208302840111600160201b83111715610cd357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610d2257600080fd5b820183602082011115610d3457600080fd5b803590602001918460208302840111600160201b83111715610d5557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610da457600080fd5b820183602082011115610db657600080fd5b803590602001918460208302840111600160201b83111715610dd757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506131a4945050505050565b348015610e2157600080fd5b506102cc60048036036040811015610e3857600080fd5b506001600160a01b0381358116916020013516613393565b61032e6004803603610140811015610e6757600080fd5b810190602081018135600160201b811115610e8157600080fd5b820183602082011115610e9357600080fd5b803590602001918460208302840111600160201b83111715610eb457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550506001600160a01b038335811694506020840135936040810135935060608101359250608081013515159160a08201359160c08101359160e082013591610100013516613477565b348015610f3b57600080fd5b50610f4461365a565b604080519485526020850193909352838301919091526060830152519081900360800190f35b348015610f7657600080fd5b506102cc60048036036040811015610f8d57600080fd5b50803590602001356001600160a01b031661366c565b348015610faf57600080fd5b506102e8613844565b348015610fc457600080fd5b506102e8613853565b348015610fd957600080fd5b5061032e60048036036020811015610ff057600080fd5b50356001600160a01b0316613862565b34801561100c57600080fd5b5061102a6004803603602081101561102357600080fd5b5035613874565b604080516001600160a01b039d8e1681529b8d1660208d01528b81019a909a5260608b019890985260808a019690965293151560a089015260c088019290925260e087015261010086015261012085015215156101408401529092166101608201529051908190036101800190f35b3480156110a557600080fd5b506102e86138e8565b3480156110ba57600080fd5b506102cc600480360360208110156110d157600080fd5b50356138f7565b6009546001600160a01b031681565b601281815481106110f457fe5b600091825260209091200154905081565b60006002600054141561114d576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b600260005561115a615b85565b6000848152601b602090815260409182902082516101c08101845281546001600160a01b03168152600182018054855181860281018601909652808652919492938581019392908301828280156111da57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116111bc575b505050918352505060028201546001600160a01b0390811660208301526003830154604083015260048301546060830152600583015460ff8082161515608085015261010091829004831660a0850152600685015460c0850152600785015460e08501526008850154828501526009850154610120850152600a850154610140850152600b90940154938416151561016084015290920482166101809091015281519192501661128e576001915050611598565b60006112a9826101400151836101600151846000015161397f565b9050806112bb57600092505050611598565b6000858152601b6020526040812080546001600160a01b0319168155906112e56001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820180546001600160a81b031990811690915560068301829055600783018290556008830182905560098301829055600a8301829055600b909201805490921690915582516020840151805161138b929190849061136457fe5b60200260200101518560400151866060015187608001518860a00151308a60e00151613b05565b9050801561143a57600183602001515111156113fb57600354602084015180516113e4926001600160a01b03169184916000906113c457fe5b60200260200101516001600160a01b0316613f9e9092919063ffffffff16565b6113f8836020015184610100015130613ff5565b90505b8261018001511561141957611414818460c00151614073565b61143a565b61143a8360c00151828560200151600187602001515103815181106113c457fe5b61144983610120015186614073565b82600001516001600160a01b03167f21435c5b618d77ff3657140cd3318e2cffaebc5e0e1b7318f56a9ba4044c3ed284602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001518c61012001516114bf8e61014001514361410190919063ffffffff16565b6101608f01516114d0904290614101565b60405180806020018c6001600160a01b031681526020018b81526020018a81526020018915158152602001886001600160a01b0316815260200187815260200186815260200185815260200184815260200183815260200182810382528d818151815260200191508051906020019060200280838360005b83811015611560578181015183820152602001611548565b505050509050019c5050505050505050505050505060405180910390a2611590836101a001518760016000614143565b600193505050505b600160005592915050565b600b6020526000908152604090205481565b61271081565b6001546001600160a01b031681565b60155481565b600a6020526000908152604090205481565b601b602052600090815260409020805460028201546003830154600484015460058501546006860154600787015460088801546009890154600a8a0154600b909a01546001600160a01b03998a169a988a16999798969760ff80881698610100988990048316989093918216929104168d565b60006002600054141561169d576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b60026000556116aa615c3a565b60008481526019602090815260409182902082516101a08101845281546001600160a01b031681526001820180548551818602810186019096528086529194929385810193929083018282801561172a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161170c575b505050918352505060028201546001600160a01b039081166020830152600383015460408301526004830154606083015260058301546080830152600683015460ff908116151560a0840152600784015460c0840152600884015460e0840152600984015461010080850191909152600a850154610120850152600b909401549081161515610140840152929092048216610160909101528151919250166117d6576001915050611598565b60006117f18261012001518361014001518460000151614249565b90508061180357600092505050611598565b600085815260196020526040812080546001600160a01b03191681559061182d6001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820181905560068201805460ff19169055600782018190556008820181905560098201819055600a820155600b0180546001600160a81b0319169055610160820151156118b0576118ab82606001518360000151614073565b6118ce565b6118ce8260000151836060015184602001516000815181106113c457fe5b6118dd82610100015185614073565b81600001516001600160a01b03167f35b638e650e2328786fb405bd69d2083dbedc018d086662e74b775b4f1dae4bf83602001518460400151856060015186608001518760a001518860c001518960e001518a610100015161194d8c61012001514361410190919063ffffffff16565b6101408d015161195e904290614101565b60405180806020018b6001600160a01b031681526020018a8152602001898152602001888152602001871515815260200186815260200185815260200184815260200183815260200182810382528c818151815260200191508051906020019060200280838360005b838110156119df5781810151838201526020016119c7565b505050509050019b50505050505050505050505060405180910390a2611a0e8261018001518660006001614143565b600192505050600160005592915050565b6002546001600160a01b03163314611a6c576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60088190556040805182815290517f21167d0d4661af93817ebce920f18986eed3d75d5e1c03f2aed05efcbafbc4529181900360200190a150565b6001546001600160a01b03163314611af4576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b611b076001600160a01b038316826142b1565b5050565b60165481565b600060026000541415611b59576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055611b66615c3a565b60008481526019602090815260409182902082516101a08101845281546001600160a01b0316815260018201805485518186028101860190965280865291949293858101939290830182828015611be657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611bc8575b505050918352505060028201546001600160a01b039081166020830152600383015460408301526004830154606083015260058301546080830152600683015460ff908116151560a0840152600784015460c0840152600884015460e0840152600984015461010080850191909152600a850154610120850152600b90940154908116151561014084015292909204821661016090910152815191925016611c92576001915050611598565b6000611cad826101200151836101400151846000015161397f565b905080611cbf57600092505050611598565b600085815260196020526040812080546001600160a01b031916815590611ce96001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820181905560068201805460ff19169055600782018190556008820181905560098201819055600a820155600b0180546001600160a81b0319169055606082015115611df657606082015160208301515160011015611da557600354606084015160208501518051611d8f936001600160a01b03169291906000906113c457fe5b611da28360200151846080015130613ff5565b90505b6000611dc53385602001518487604001518860c001518960a00151614396565b60035460208601518051929350611df3926001600160a01b039092169184919060001981019081106113c457fe5b50505b815160208301518051611e349291906000198101908110611e1357fe5b602002602001015184604001518560a001518660c001518760e00151614475565b611e4382610100015185614073565b81600001516001600160a01b03167f1be316b94d38c07bd41cdb4913772d0a0a82802786a2f8b657b6e85dbcdfc64183602001518460400151856060015186608001518760a001518860c001518960e001518a6101000151611eb38c61012001514361410190919063ffffffff16565b6101408d0151611ec4904290614101565b60405180806020018b6001600160a01b031681526020018a8152602001898152602001888152602001871515815260200186815260200185815260200184815260200183815260200182810382528c818151815260200191508051906020019060200280838360005b83811015611f45578181015183820152602001611f2d565b505050509050019b50505050505050505050505060405180910390a2611a0e82610180015186600180614143565b6002546001600160a01b03163314611fc0576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60148290556015819055604080518381526020810183905281517febb0f666150f4be5b60c45df8f3e49992510b0128027fe58eea6110f296493bc929181900390910190a15050565b6002546001600160a01b03163314612056576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b6001600160a01b038216600081815260176020908152604091829020805460ff1916851515908117909155825190815291517ffbabc02389290a451c6e600d05bf9887b99bfad39d8e1237e4e3df042e4941fe9281900390910190a25050565b60176020526000908152604090205460ff1681565b600f5481565b60115460ff1681565b6006546001600160a01b031681565b6002546001600160a01b03163314612136576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b600e839055600f8290556010819055604080518481526020810184905280820183905290517fb98e759701eaca2e60c25e91109003c1c7442ef731b5d569037063005da8254d9181900360600190a1505050565b601381815481106110f457fe5b6002546001600160a01b031633146121e4576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60078190556040805182815290517f974fd3c1fcb4653dfc4fb740c4c692cd212d55c28f163f310128cb64d83006759181900360200190a150565b600e5481565b60006002600054141561226d576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055600d548410156122af576040805162461bcd60e51b815260206004820152600360248201526266656560e81b604482015290519081900360640190fd5b833410156122ea576040805162461bcd60e51b81526020600482015260036024820152621d985b60ea1b604482015290519081900360640190fd5b8951600114806122fb575089516002145b612332576040805162461bcd60e51b81526020600482015260036024820152623632b760e91b604482015290519081900360640190fd5b6006548a516001600160a01b03909116908b9060009061234e57fe5b60200260200101516001600160a01b03161461239a576040805162461bcd60e51b815260206004808301919091526024820152630e0c2e8d60e31b604482015290519081900360640190fd5b6123a261487d565b6123ab836148e9565b60006123b73486614101565b90506123cd338c8c848d8d8d8d8d60018d61495d565b60016000559b9a5050505050505050505050565b60606123eb615b85565b6000838152601b602090815260409182902082516101c08101845281546001600160a01b031681526001820180548551818602810186019096528086529194929385810193929083018282801561246b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161244d575b505050918352505060028201546001600160a01b039081166020808401919091526003840154604084015260048401546060840152600584015460ff8082161515608086015261010091829004841660a0860152600686015460c0860152600786015460e08601526008860154828601526009860154610120860152600a860154610140860152600b909501549485161515610160850152909304166101809091015201519392505050565b60006002600054141561255f576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b600260005561256c615b85565b6000848152601b602090815260409182902082516101c08101845281546001600160a01b03168152600182018054855181860281018601909652808652919492938581019392908301828280156125ec57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116125ce575b505050918352505060028201546001600160a01b0390811660208301526003830154604083015260048301546060830152600583015460ff8082161515608085015261010091829004831660a0850152600685015460c0850152600785015460e08501526008850154828501526009850154610120850152600a850154610140850152600b9094015493841615156101608401529092048216610180909101528151919250166126a0576001915050611598565b60006126bb8261014001518361016001518460000151614249565b9050806126cd57600092505050611598565b6000858152601b6020526040812080546001600160a01b0319168155906126f76001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820180546001600160a81b031990811690915560068301829055600783018290556008830182905560098301829055600a830191909155600b9091018054909116905561012082015161276f9085614073565b81600001516001600160a01b03167f87abfd78e844f28318363bdf3da99eab2f4a2da9ff7ae365484507f7b6c3f80583602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b61012001516127e58d61014001514361410190919063ffffffff16565b6101608e01516127f6904290614101565b60405180806020018c6001600160a01b031681526020018b81526020018a81526020018915158152602001886001600160a01b0316815260200187815260200186815260200185815260200184815260200183815260200182810382528d818151815260200191508051906020019060200280838360005b8381101561288657818101518382015260200161286e565b505050509050019c5050505050505050505050505060405180910390a2611a0e826101a0015186600080614143565b6000828260405160200180836001600160a01b031660601b8152601401828152602001925050506040516020818303038152906040528051906020012090505b92915050565b60186020526000908152604090205481565b600d5481565b6004546001600160a01b031681565b60075481565b6001546001600160a01b03163314612975576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b600280546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a19181900360200190a150565b600060026000541415612a11576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055600d54841015612a53576040805162461bcd60e51b815260206004820152600360248201526266656560e81b604482015290519081900360640190fd5b833414612a8d576040805162461bcd60e51b81526020600482015260036024820152621d985b60ea1b604482015290519081900360640190fd5b8b5160011480612a9e57508b516002145b612ad5576040805162461bcd60e51b81526020600482015260036024820152623632b760e91b604482015290519081900360640190fd5b8215612b48576006548c516001600160a01b03909116908d906000198101908110612afc57fe5b60200260200101516001600160a01b031614612b48576040805162461bcd60e51b815260206004808301919091526024820152630e0c2e8d60e31b604482015290519081900360640190fd5b612b5061487d565b612b64338d8d8d8d8d8d8d8d8d8d8d614af4565b60016000559c9b505050505050505050505050565b6002546001600160a01b03163314612bc6576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b6011805482151560ff19909116811790915560408051918252517f4eb87a5935d402aa24c01b45bfb30adefcd2328b480f2d967864de4b64ea929f9181900360200190a150565b6002546001600160a01b03163314612c5a576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60168190556040805182815290517f22bd2c9f980325d046be74aaef5fc76df4a2bc3fbc7c5a1200fcc79fe80dab6c9181900360200190a150565b6060612c9f615c3a565b60008381526019602090815260409182902082516101a08101845281546001600160a01b0316815260018201805485518186028101860190965280865291949293858101939290830182828015612d1f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612d01575b505050918352505060028201546001600160a01b03908116602080840191909152600384015460408401526004840154606084015260058401546080840152600684015460ff908116151560a0850152600785015460c0850152600885015460e0850152600985015461010080860191909152600a860154610120860152600b90950154908116151561014085015293909304166101609091015201519392505050565b600c6020526000908152604090205481565b60085481565b3360009081526017602052604090205460ff16612e25576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b601454601254808210612e39575050611b07565b80841115612e45578093505b83821015612fab57600060128381548110612e5c57fe5b90600052602060002001549050306001600160a01b03166327b42c0f82866040518363ffffffff1660e01b815260040180838152602001826001600160a01b0316815260200192505050602060405180830381600087803b158015612ec057600080fd5b505af1925050508015612ee557506040513d6020811015612ee057600080fd5b505160015b612f79576040805163225fc9fd60e01b8152600481018390526001600160a01b03861660248201529051309163225fc9fd9160448083019260209291908290030181600087803b158015612f3857600080fd5b505af1925050508015612f5d57506040513d6020811015612f5857600080fd5b505160015b612f6657612f74565b80612f72575050612fab565b505b612f87565b80612f85575050612fab565b505b60128381548110612f9457fe5b600091825260208220015550600190910190612e45565b506014555050565b60145481565b6002546001600160a01b03163314613006576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b600980546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f828abcccea18192c21d645e575652c49e20b986dab777906fc473d056b01b6a89181900360200190a150565b60105481565b6001546001600160a01b031633146130ad576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001546001600160a01b0316331461311c576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b826001600160a01b031663095ea7b383836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561317357600080fd5b505af1158015613187573d6000803e3d6000fd5b505050506040513d602081101561319d57600080fd5b5050505050565b6002546001600160a01b031633146131f1576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60005b835181101561328c57600084828151811061320b57fe5b6020026020010151905083828151811061322157fe5b6020026020010151600b6000836001600160a01b03166001600160a01b031681526020019081526020016000208190555082828151811061325e57fe5b6020908102919091018101516001600160a01b039092166000908152600c90915260409020556001016131f4565b507fae32d569b058895b9620d6552b09aaffedc9a6f396be4d595a224ad09f8b213983838360405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156132f95781810151838201526020016132e1565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015613338578181015183820152602001613320565b50505050905001848103825285818151815260200191508051906020019060200280838360005b8381101561337757818101518382015260200161335f565b50505050905001965050505050505060405180910390a1505050565b6002546001600160a01b031633146133e0576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b6001600160a01b0382166000908152600a6020526040902054806134045750611b07565b6001600160a01b0383166000818152600a6020526040812055613428908383613f9e565b604080516001600160a01b0380861682528416602082015280820183905290517f4f1b51dd7a2fcb861aa2670f668be66835c4ee12b4bbbf037e4d0018f39819e49181900360600190a1505050565b6000600260005414156134bf576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055600d54841015613501576040805162461bcd60e51b815260206004820152600360248201526266656560e81b604482015290519081900360640190fd5b83341461353b576040805162461bcd60e51b81526020600482015260036024820152621d985b60ea1b604482015290519081900360640190fd5b8a516001148061354c57508a516002145b613583576040805162461bcd60e51b81526020600482015260036024820152623632b760e91b604482015290519081900360640190fd5b61358b61487d565b613594836148e9565b8815613646576005548b516001600160a01b0390911690631b827878908d906000906135bc57fe5b602002602001015133308d6040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b03168152602001828152602001945050505050600060405180830381600087803b15801561362d57600080fd5b505af1158015613641573d6000803e3d6000fd5b505050505b6123cd338c8c8c8c8c8c8c8c60008c61495d565b60145460125460155460135490919293565b3360009081526017602052604090205460ff166136b6576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b6015546013548082106136ca575050611b07565b808411156136d6578093505b8382101561383c576000601383815481106136ed57fe5b90600052602060002001549050306001600160a01b0316630d4d003d82866040518363ffffffff1660e01b815260040180838152602001826001600160a01b0316815260200192505050602060405180830381600087803b15801561375157600080fd5b505af192505050801561377657506040513d602081101561377157600080fd5b505160015b61380a5760408051633051b17160e11b8152600481018390526001600160a01b0386166024820152905130916360a362e29160448083019260209291908290030181600087803b1580156137c957600080fd5b505af19250505080156137ee57506040513d60208110156137e957600080fd5b505160015b6137f757613805565b8061380357505061383c565b505b613818565b8061381657505061383c565b505b6013838154811061382557fe5b6000918252602082200155506001909101906136d6565b506015555050565b6002546001600160a01b031681565b6005546001600160a01b031681565b601a6020526000908152604090205481565b6019602052600090815260409020805460028201546003830154600484015460058501546006860154600787015460088801546009890154600a8a0154600b909a01546001600160a01b03998a169a988a169997989697959660ff958616969495939492939092908216916101009004168c565b6003546001600160a01b031681565b6002546001600160a01b03163314613944576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b600d8190556040805182815290517f52a8358457e20bbb36e4086b83fb0749599f1893fe4c35a876c46dc4886d12db9181900360200190a150565b60004261399760105485614cce90919063ffffffff16565b116139d3576040805162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b604482015290519081900360640190fd5b6000333014806139f257503360009081526017602052604090205460ff165b60115490915060ff16158015613a06575080155b15613a3e576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b8015613a645743613a5a600e5487614cce90919063ffffffff16565b1115915050613afe565b336001600160a01b03841614613aa7576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b42613abd600f5486614cce90919063ffffffff16565b1115613af8576040805162461bcd60e51b815260206004820152600560248201526464656c617960d81b604482015290519081900360640190fd5b60019150505b9392505050565b6003546000906001600160a01b03168185613b9857816001600160a01b031663e124e6d28a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613b6757600080fd5b505afa158015613b7b573d6000803e3d6000fd5b505050506040513d6020811015613b9157600080fd5b5051613c12565b816001600160a01b03166381a612d68a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613be557600080fd5b505afa158015613bf9573d6000803e3d6000fd5b505050506040513d6020811015613c0f57600080fd5b50515b90508515613c5e5783811015613c595760405162461bcd60e51b8152600401808060200182810382526030815260200180615e546030913960400191505060405180910390fd5b613c9d565b83811115613c9d5760405162461bcd60e51b8152600401808060200182810382526031815260200180615ef46031913960400191505060405180910390fd5b6000826001600160a01b03166312d43a516040518163ffffffff1660e01b815260040160206040518083038186803b158015613cd857600080fd5b505afa158015613cec573d6000803e3d6000fd5b505050506040513d6020811015613d0257600080fd5b81019080805190602001909291905050509050600460009054906101000a90046001600160a01b03166001600160a01b031663f3238cec8d8d8d8b8d8860006040518863ffffffff1660e01b815260040180886001600160a01b03168152602001876001600160a01b03168152602001866001600160a01b0316815260200185151581526020018481526020018381526020018215158152602001975050505050505050600060405180830381600087803b158015613dc057600080fd5b505af1158015613dd4573d6000803e3d6000fd5b50505050806001600160a01b0316636d63c1d0846040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015613e2757600080fd5b505af1158015613e3b573d6000803e3d6000fd5b505050506000600560009054906101000a90046001600160a01b03166001600160a01b0316632662166b8e8e8e8e8e8e8e6040518863ffffffff1660e01b815260040180886001600160a01b03168152602001876001600160a01b03168152602001866001600160a01b031681526020018581526020018481526020018315158152602001826001600160a01b03168152602001975050505050505050602060405180830381600087803b158015613ef257600080fd5b505af1158015613f06573d6000803e3d6000fd5b505050506040513d6020811015613f1c57600080fd5b50516040805163d3c87bbb60e01b81526001600160a01b03878116600483015291519293509084169163d3c87bbb9160248082019260009290919082900301818387803b158015613f6c57600080fd5b505af1158015613f80573d6000803e3d6000fd5b50505050613f8e8d8a614d26565b9c9b505050505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613ff0908490614eaf565b505050565b600083516002141561403c576140358460008151811061401157fe5b60200260200101518560018151811061402657fe5b60200260200101518585614f60565b9050613afe565b60405162461bcd60e51b8152600401808060200182810382526029815260200180615f256029913960400191505060405180910390fd5b60065460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921691632e1a7d4d9160248082019260009290919082900301818387803b1580156140c057600080fd5b505af11580156140d4573d6000803e3d6000fd5b50506040516001600160a01b038416925084156108fc02915084906000818181858888f150505050505050565b6000613afe83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250615036565b6001600160a01b03841661415657614243565b614168846001600160a01b03166150cd565b61417157614243565b6016548061417f5750614243565b6000856001600160a01b031663edf3daec838787876040518563ffffffff1660e01b815260040180848152602001831515815260200182151581526020019350505050600060405180830381600088803b1580156141dc57600080fd5b5087f1935050505080156141ee575060015b6141f7576141fb565b5060015b604080516001600160a01b0388168152821515602082015281517f46ddbd62fc1a7626fe9c43026cb0694aec0b031fe81ac66fb4cfe9381dc6fe72929181900390910190a150505b50505050565b600080333014806139f257503360009081526017602052604090205460ff1660115490915060ff16158015613a06575080613a3e576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b80471015614306576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015290519081900360640190fd5b6040516000906001600160a01b0384169083908381818185875af1925050503d8060008114614351576040519150601f19603f3d011682016040523d82523d6000602084013e614356565b606091505b5050905080613ff05760405162461bcd60e51b815260040180806020018281038252603a815260200180615df4603a913960400191505060405180910390fd5b6000806143a78888888888886150d3565b905080156144665760006143de6127106143d86143d160075461271061410190919063ffffffff16565b8a906152b0565b90615309565b905060006143ec8883614101565b905060008960018b51038151811061440057fe5b6020026020010151905061444282600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002054614cce90919063ffffffff16565b6001600160a01b039091166000908152600a602052604090205550915061446b9050565b859150505b9695505050505050565b6003546001600160a01b031660008361450657816001600160a01b03166381a612d6876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156144d557600080fd5b505afa1580156144e9573d6000803e3d6000fd5b505050506040513d60208110156144ff57600080fd5b5051614580565b816001600160a01b031663e124e6d2876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561455357600080fd5b505afa158015614567573d6000803e3d6000fd5b505050506040513d602081101561457d57600080fd5b50515b905083156145cc57828111156145c75760405162461bcd60e51b8152600401808060200182810382526031815260200180615ef46031913960400191505060405180910390fd5b61460b565b8281101561460b5760405162461bcd60e51b8152600401808060200182810382526030815260200180615e546030913960400191505060405180910390fd5b614616868587615348565b6000826001600160a01b03166312d43a516040518163ffffffff1660e01b815260040160206040518083038186803b15801561465157600080fd5b505afa158015614665573d6000803e3d6000fd5b505050506040513d602081101561467b57600080fd5b50516004805460408051633cc8e33b60e21b81526001600160a01b038e8116948201949094528c841660248201528b841660448201528915156064820152608481018b905260a48101879052600160c4820152905193945091169163f3238cec9160e48082019260009290919082900301818387803b1580156146fd57600080fd5b505af1158015614711573d6000803e3d6000fd5b50505050806001600160a01b0316636d63c1d0846040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561476457600080fd5b505af1158015614778573d6000803e3d6000fd5b505060055460408051630f8ee8bb60e11b81526001600160a01b038e811660048301528d811660248301528c81166044830152606482018c90528a151560848301529151919092169350631f1dd176925060a480830192600092919082900301818387803b1580156147e957600080fd5b505af11580156147fd573d6000803e3d6000fd5b50505050806001600160a01b031663d3c87bbb846040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561485057600080fd5b505af1158015614864573d6000803e3d6000fd5b5050505061487289876154fa565b505050505050505050565b34156148e757600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156148d357600080fd5b505af115801561319d573d6000803e3d6000fd5b565b801580159061490257506009546001600160a01b031615155b1561495a57600954604080516356b4b2ad60e01b81523360048201526024810184905290516001600160a01b03909216916356b4b2ad9160448082019260009290919082900301818387803b1580156148d357600080fd5b50565b6000614967615c3a565b604051806101a001604052808e6001600160a01b031681526020018d81526020018c6001600160a01b031681526020018b81526020018a815260200189815260200188151581526020018781526020018681526020014381526020014281526020018515158152602001846001600160a01b031681525090506000806149ec83615600565b915091508e6001600160a01b03167f5265bc4952da402633b3fc35f67ab4245493a0ab94dd8ab123667c8d45a4485c8f8f8f8f8f8f8f8f8b60016012805490500343423a60405180806020018e6001600160a01b031681526020018d81526020018c81526020018b81526020018a1515815260200189815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528f818151815260200191508051906020019060200280838360005b83811015614ac3578181015183820152602001614aab565b505050509050019e50505050505050505050505050505060405180910390a29e9d5050505050505050505050505050565b6000614afe615b85565b604051806101c001604052808f6001600160a01b031681526020018e81526020018d6001600160a01b031681526020018c81526020018b81526020018a15158152602001896001600160a01b031681526020018881526020018781526020018681526020014381526020014281526020018515158152602001846001600160a01b03168152509050600080614b928361579e565b9150915082600001516001600160a01b03167f81ed0476a7e785a9e4728fffd679ea97176ca1ac85e1003462558bb5677da57b84602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001518c61012001518c600160138054905003434260405180806020018e6001600160a01b031681526020018d81526020018c81526020018b151581526020018a6001600160a01b0316815260200189815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528f818151815260200191508051906020019060200280838360005b83811015614c9c578181015183820152602001614c84565b505050509050019e50505050505050505050505050505060405180910390a29f9e505050505050505050505050505050565b600082820183811015613afe576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6009546001600160a01b031680614d3d5750611b07565b600080826001600160a01b031663534ef883866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b158015614d8c57600080fd5b505afa158015614da0573d6000803e3d6000fd5b505050506040513d6040811015614db657600080fd5b508051602090910151909250905081614dd157505050611b07565b7f474c763ff84bf2c2039a6d9fea955ecd0f724030e3c365b91169c6a16fe751b78585600360009054906101000a90046001600160a01b03166001600160a01b031663318bc6896040518163ffffffff1660e01b815260040160206040518083038186803b158015614e4257600080fd5b505afa158015614e56573d6000803e3d6000fd5b505050506040513d6020811015614e6c57600080fd5b5051604080516001600160a01b03948516815260208101939093528281019190915260608201869052918416608082015290519081900360a00190a15050505050565b6060614f04826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166159479092919063ffffffff16565b805190915015613ff057808060200190516020811015614f2357600080fd5b5051613ff05760405162461bcd60e51b815260040180806020018281038252602a815260200180615f71602a913960400191505060405180910390fd5b60035460408051634998b10960e11b81526001600160a01b03878116600483015286811660248301528481166044830152915160009384931691639331621291606480830192602092919082900301818787803b158015614fc057600080fd5b505af1158015614fd4573d6000803e3d6000fd5b505050506040513d6020811015614fea57600080fd5b505190508381101561502d5760405162461bcd60e51b815260040180806020018281038252602b815260200180615dc9602b913960400191505060405180910390fd5b95945050505050565b600081848411156150c55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561508a578181015183820152602001615072565b50505050905090810190601f1680156150b75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b3b151590565b6000826150e25750600061446b565b816150ef5750600161446b565b60008660018851038151811061510157fe5b602090810291909101015160035460408051634a3f088d60e01b81526001600160a01b038c81166004830152808516602483015289811660448301528815156064830152915193945091169160009182918491634a3f088d91608480830192610100929190829003018186803b15801561517a57600080fd5b505afa15801561518e573d6000803e3d6000fd5b505050506040513d6101008110156151a557600080fd5b5080516020909101519092509050816151c557600094505050505061446b565b60006151d18388614cce565b90506000846001600160a01b0316630a48d5a9878d6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b15801561522a57600080fd5b505afa15801561523e573d6000803e3d6000fd5b505050506040513d602081101561525457600080fd5b5051905060006152648483614cce565b90506000615278856143d8886127106152b0565b90506000615299836143d860085461271001886152b090919063ffffffff16565b919091109f9e505050505050505050505050505050565b6000826152bf575060006128f5565b828202828482816152cc57fe5b0414613afe5760405162461bcd60e51b8152600401808060200182810382526021815260200180615ed36021913960400191505060405180910390fd5b6000613afe83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b81525061595e565b8061535257613ff0565b8115615445576001600160a01b0383166000908152600b6020526040902054801580159061540357506003546040805163783a2b6760e11b81526001600160a01b0387811660048301529151849361540193879391169163f07456ce91602480820192602092909190829003018186803b1580156153cf57600080fd5b505afa1580156153e3573d6000803e3d6000fd5b505050506040513d60208110156153f957600080fd5b505190614cce565b115b1561543f5760405162461bcd60e51b815260040180806020018281038252602e815260200180615d5b602e913960400191505060405180910390fd5b50613ff0565b6001600160a01b0383166000908152600c602052604090205480158015906154be57506003546040805163114f1b5560e31b81526001600160a01b038781166004830152915184936154bc938793911691638a78daa891602480820192602092909190829003018186803b1580156153cf57600080fd5b115b156142435760405162461bcd60e51b815260040180806020018281038252602f815260200180615ea4602f913960400191505060405180910390fd5b6009546001600160a01b0316806155115750611b07565b600080826001600160a01b031663534ef883866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b15801561556057600080fd5b505afa158015615574573d6000803e3d6000fd5b505050506040513d604081101561558a57600080fd5b5080516020918201516003546040805163318bc68960e01b815290519396509194507fc2414023ce7002ee98557d1e7be21e5559073336f2217ee5f9b2e50fd85f71ee93899389936001600160a01b039093169263318bc689926004808301939192829003018186803b158015614e4257600080fd5b80516001600160a01b03811660009081526018602052604081205490918291829061562c906001614cce565b6001600160a01b038316600090815260186020526040812082905590915061565483836128b5565b6000818152601960209081526040909120885181546001600160a01b0319166001600160a01b03909116178155888201518051939450899391926156a092600185019290910190615cc1565b5060408201516002820180546001600160a01b039283166001600160a01b0319909116179055606083015160038301556080830151600483015560a0830151600583015560c083015160068301805491151560ff1992831617905560e084015160078401556101008085015160088501556101208501516009850155610140850151600a850155610160850151600b90940180546101809096015190931602610100600160a81b0319931515949091169390931791909116919091179055601280546001810182556000919091527fbb8a6a4669ba250d26cd7a459eca9d215f8307e33aebe50379bc5a3617ec344401819055909350915050915091565b80516001600160a01b0381166000908152601a60205260408120549091829182906157ca906001614cce565b6001600160a01b0383166000908152601a602052604081208290559091506157f283836128b5565b6000818152601b60209081526040909120885181546001600160a01b0319166001600160a01b039091161781558882015180519394508993919261583e92600185019290910190615cc1565b5060408201516002820180546001600160a01b039283166001600160a01b0319909116179055606083015160038301556080830151600483015560a083015160058301805460c08601518416610100908102610100600160a81b031994151560ff199384161785161790925560e0860151600686015581860151600786015561012086015160088601556101408601516009860155610160860151600a860155610180860151600b90950180546101a090970151909416909102931515941693909317909216179055601380546001810182556000919091527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a09001819055909350915050915091565b606061595684846000856159c3565b949350505050565b600081836159ad5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561508a578181015183820152602001615072565b5060008385816159b957fe5b0495945050505050565b606082471015615a045760405162461bcd60e51b8152600401808060200182810382526026815260200180615e2e6026913960400191505060405180910390fd5b615a0d856150cd565b615a5e576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310615a9d5780518252601f199092019160209182019101615a7e565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114615aff576040519150601f19603f3d011682016040523d82523d6000602084013e615b04565b606091505b5091509150615b14828286615b1f565b979650505050505050565b60608315615b2e575081613afe565b825115615b3e5782518084602001fd5b60405162461bcd60e51b815260206004820181815284516024840152845185939192839260440191908501908083836000831561508a578181015183820152602001615072565b604051806101c0016040528060006001600160a01b031681526020016060815260200160006001600160a01b03168152602001600081526020016000815260200160001515815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681525090565b508054600082559060005260206000209081019061495a9190615d26565b604051806101a0016040528060006001600160a01b031681526020016060815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000151581526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681525090565b828054828255906000526020600020908101928215615d16579160200282015b82811115615d1657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615ce1565b50615d22929150615d3b565b5090565b5b80821115615d225760008155600101615d27565b5b80821115615d225780546001600160a01b0319168155600101615d3c56fe42617365506f736974696f6e4d616e616765723a206d617820676c6f62616c206c6f6e67732065786365656465645265656e7472616e637947756172643a207265656e7472616e742063616c6c00476f7665726e61626c653a20666f7262696464656e000000000000000000000042617365506f736974696f6e4d616e616765723a20696e73756666696369656e7420616d6f756e744f7574416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d61792068617665207265766572746564416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c42617365506f736974696f6e4d616e616765723a206d61726b207072696365206c6f776572207468616e206c696d697442617365506f736974696f6e4d616e616765723a20666f7262696464656e000042617365506f736974696f6e4d616e616765723a206d617820676c6f62616c2073686f727473206578636565646564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7742617365506f736974696f6e4d616e616765723a206d61726b20707269636520686967686572207468616e206c696d697442617365506f736974696f6e4d616e616765723a20696e76616c6964205f706174682e6c656e67746842617365506f736974696f6e4d616e616765723a20696e76616c69642073656e6465725361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a26469706673582212209ce5a3b6726e45235a68f6f370dd0252abe63f1c6342dc561eba4b4a5871f32764736f6c634300060c0033000000000000000000000000489ee077994b6658eafa855c308275ead8097c4a000000000000000000000000abbc5f99639c9b6bcb58544ddf04efa6802f406400000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000f58eec83ba28ddd79390b9e90c4d3ebff1d434da000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a4000 \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/Reader.abi b/src/Managing.Tools.ABI/Gmx/core/Reader.abi new file mode 100644 index 0000000..91f0034 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/Reader.abi @@ -0,0 +1 @@ +[{"inputs":[],"name":"BASIS_POINTS_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POSITION_PROPS_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDG_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IVault","name":"_vault","type":"address"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IVault","name":"_vault","type":"address"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"}],"name":"getFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getFees","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"uint256","name":"_usdgAmount","type":"uint256"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getFullVaultTokenInfo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getFundingRates","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IVault","name":"_vault","type":"address"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"}],"name":"getMaxAmountIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getPairInfo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"address[]","name":"_collateralTokens","type":"address[]"},{"internalType":"address[]","name":"_indexTokens","type":"address[]"},{"internalType":"bool[]","name":"_isLong","type":"bool[]"}],"name":"getPositions","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IVaultPriceFeed","name":"_priceFeed","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getPrices","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address[]","name":"_yieldTrackers","type":"address[]"}],"name":"getStakingInfo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getTokenBalances","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getTokenBalancesWithSupplies","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address[]","name":"_excludedAccounts","type":"address[]"}],"name":"getTokenSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address[]","name":"_accounts","type":"address[]"}],"name":"getTotalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_yieldTokens","type":"address[]"}],"name":"getTotalStaked","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"uint256","name":"_usdgAmount","type":"uint256"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getVaultTokenInfo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"uint256","name":"_usdgAmount","type":"uint256"},{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"getVaultTokenInfoV2","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address[]","name":"_vesters","type":"address[]"}],"name":"getVestingInfo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasMaxGlobalShortSizes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_hasMaxGlobalShortSizes","type":"bool"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/Reader.bin b/src/Managing.Tools.ABI/Gmx/core/Reader.bin new file mode 100644 index 0000000..e68db87 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/Reader.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50600080546001600160a01b031916331790556159ff80620000336000396000f3fe608060405234801561001057600080fd5b506004361061013e5760003560e01c8063126082cf1461014357806312d43a511461015d57806320542568146101815780632413c8c1146102925780632ac0184c146103335780632e3e3342146103e45780633613d52714610495578063440f018c1461054657806348211934146105a05780637b906e931461065157806386d4d0f514610712578063870d917c146107c35780638e83ca32146107cb578063937a0be81461088c57806395082d251461093d57806395a7535a146109455780639b0183c3146109ff578063a4543ead14610a20578063ad7e549714610ad1578063c6f1d67614610ad9578063cfad57a214610af5578063d7176ca914610b1b578063d802178e14610b70578063dc383cab14610c21578063f3535e6c14610ddf578063fbdb05ca14610e17575b600080fd5b61014b610ec8565b60408051918252519081900360200190f35b610165610ece565b604080516001600160a01b039092168252519081900360200190f35b6102426004803603608081101561019757600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b8111156101d157600080fd5b8201836020820111156101e357600080fd5b803590602001918460208302840111600160201b8311171561020457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610edd945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561027e578181015183820152602001610266565b505050509050019250505060405180910390f35b610242600480360360208110156102a857600080fd5b810190602081018135600160201b8111156102c257600080fd5b8201836020820111156102d457600080fd5b803590602001918460208302840111600160201b831117156102f557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506115eb945050505050565b61014b6004803603604081101561034957600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561037357600080fd5b82018360208201111561038557600080fd5b803590602001918460208302840111600160201b831117156103a657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506116e4945050505050565b610242600480360360408110156103fa57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561042457600080fd5b82018360208201111561043657600080fd5b803590602001918460208302840111600160201b8311171561045757600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061180f945050505050565b610242600480360360408110156104ab57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156104d557600080fd5b8201836020820111156104e757600080fd5b803590602001918460208302840111600160201b8311171561050857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611a14945050505050565b6105826004803603608081101561055c57600080fd5b506001600160a01b03813581169160208101358216916040820135169060600135611e4e565b60408051938452602084019290925282820152519081900360600190f35b610242600480360360408110156105b657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156105e057600080fd5b8201836020820111156105f257600080fd5b803590602001918460208302840111600160201b8311171561061357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506123b3945050505050565b6102426004803603608081101561066757600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b8111156106a157600080fd5b8201836020820111156106b357600080fd5b803590602001918460208302840111600160201b831117156106d457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612867945050505050565b6102426004803603604081101561072857600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561075257600080fd5b82018360208201111561076457600080fd5b803590602001918460208302840111600160201b8311171561078557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550613098945050505050565b61014b613198565b610242600480360360808110156107e157600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b81111561081b57600080fd5b82018360208201111561082d57600080fd5b803590602001918460208302840111600160201b8311171561084e57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061319d945050505050565b610242600480360360408110156108a257600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156108cc57600080fd5b8201836020820111156108de57600080fd5b803590602001918460208302840111600160201b831117156108ff57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550613b1a945050505050565b61014b613caf565b6102426004803603606081101561095b57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561098e57600080fd5b8201836020820111156109a057600080fd5b803590602001918460208302840111600160201b831117156109c157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550613cbf945050505050565b610a1e60048036036020811015610a1557600080fd5b5035151561418d565b005b61024260048036036040811015610a3657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610a6057600080fd5b820183602082011115610a7257600080fd5b803590602001918460208302840111600160201b83111715610a9357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550614202945050505050565b61014b61446e565b610ae1614473565b604080519115158252519081900360200190f35b610a1e60048036036020811015610b0b57600080fd5b50356001600160a01b0316614483565b610b5760048036036080811015610b3157600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356144fc565b6040805192835260208301919091528051918290030190f35b61024260048036036040811015610b8657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610bb057600080fd5b820183602082011115610bc257600080fd5b803590602001918460208302840111600160201b83111715610be357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550614bb2945050505050565b610242600480360360a0811015610c3757600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b811115610c6a57600080fd5b820183602082011115610c7c57600080fd5b803590602001918460208302840111600160201b83111715610c9d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610cec57600080fd5b820183602082011115610cfe57600080fd5b803590602001918460208302840111600160201b83111715610d1f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610d6e57600080fd5b820183602082011115610d8057600080fd5b803590602001918460208302840111600160201b83111715610da157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550614cfe945050505050565b61014b60048036036060811015610df557600080fd5b506001600160a01b0381358116916020810135821691604090910135166150fa565b61014b60048036036040811015610e2d57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610e5757600080fd5b820183602082011115610e6957600080fd5b803590602001918460208302840111600160201b83111715610e8a57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506156c1945050505050565b61271081565b6000546001600160a01b031681565b60606000600a905060008690506000816001600160a01b031663741bef1a6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f2557600080fd5b505afa158015610f39573d6000803e3d6000fd5b505050506040513d6020811015610f4f57600080fd5b5051855190915060609084026001600160401b0381118015610f7057600080fd5b50604051908082528060200260200182016040528015610f9a578160200160208202803683370190505b50905060005b86518110156115de576000878281518110610fb757fe5b6020026020010151905060006001600160a01b0316816001600160a01b03161415610fdf5750885b846001600160a01b03166352f55eed826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561102c57600080fd5b505afa158015611040573d6000803e3d6000fd5b505050506040513d602081101561105657600080fd5b50518351849084890290811061106857fe5b602002602001018181525050846001600160a01b031663c3c7b9e9826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156110c157600080fd5b505afa1580156110d5573d6000803e3d6000fd5b505050506040513d60208110156110eb57600080fd5b5051835184906001858a020190811061110057fe5b602002602001018181525050846001600160a01b0316631aa4ace5826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561115957600080fd5b505afa15801561116d573d6000803e3d6000fd5b505050506040513d602081101561118357600080fd5b5051835184906002858a020190811061119857fe5b602002602001018181525050846001600160a01b0316632c668ec1828b6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b1580156111f957600080fd5b505afa15801561120d573d6000803e3d6000fd5b505050506040513d602081101561122357600080fd5b5051835184906003858a020190811061123857fe5b602002602001018181525050846001600160a01b031663ab2f3ad4826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561129157600080fd5b505afa1580156112a5573d6000803e3d6000fd5b505050506040513d60208110156112bb57600080fd5b5051835184906004858a02019081106112d057fe5b602002602001018181525050846001600160a01b03166381a612d6826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561132957600080fd5b505afa15801561133d573d6000803e3d6000fd5b505050506040513d602081101561135357600080fd5b5051835184906005858a020190811061136857fe5b602002602001018181525050846001600160a01b031663e124e6d2826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113c157600080fd5b505afa1580156113d5573d6000803e3d6000fd5b505050506040513d60208110156113eb57600080fd5b5051835184906006858a020190811061140057fe5b602002602001018181525050846001600160a01b031663f07456ce826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561145957600080fd5b505afa15801561146d573d6000803e3d6000fd5b505050506040513d602081101561148357600080fd5b5051835184906007858a020190811061149857fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600060248301529151918716926356c8c2c192604480840193829003018186803b1580156114f257600080fd5b505afa158015611506573d6000803e3d6000fd5b505050506040513d602081101561151c57600080fd5b5051835184906008858a020190811061153157fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600160248301529151918716926356c8c2c192604480840193829003018186803b15801561158b57600080fd5b505afa15801561159f573d6000803e3d6000fd5b505050506040513d60208110156115b557600080fd5b5051835184906009858a02019081106115ca57fe5b602090810291909101015250600101610fa0565b5098975050505050505050565b60608082516001600160401b038111801561160557600080fd5b5060405190808252806020026020018201604052801561162f578160200160208202803683370190505b50905060005b83518110156116dd57600084828151811061164c57fe5b60200260200101519050806001600160a01b031663817b1cd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561168f57600080fd5b505afa1580156116a3573d6000803e3d6000fd5b505050506040513d60208110156116b957600080fd5b505183518490849081106116c957fe5b602090810291909101015250600101611635565b5092915050565b600080836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561172057600080fd5b505afa158015611734573d6000803e3d6000fd5b505050506040513d602081101561174a57600080fd5b5051905060005b835181101561180557600084828151811061176857fe5b602002602001015190506000866001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156117c157600080fd5b505afa1580156117d5573d6000803e3d6000fd5b505050506040513d60208110156117eb57600080fd5b505190506117f9848261577a565b93505050600101611751565b5090505b92915050565b60606000600290506060818451026001600160401b038111801561183257600080fd5b5060405190808252806020026020018201604052801561185c578160200160208202803683370190505b50905060005b8451811015611a0b57600085828151811061187957fe5b6020026020010151905060006001600160a01b0316816001600160a01b031614156118e857866001600160a01b03163183858402815181106118b757fe5b602002602001018181525050600083858402600101815181106118d657fe5b60200260200101818152505050611a03565b806001600160a01b03166370a08231886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561193557600080fd5b505afa158015611949573d6000803e3d6000fd5b505050506040513d602081101561195f57600080fd5b50518351849084870290811061197157fe5b602002602001018181525050806001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119b657600080fd5b505afa1580156119ca573d6000803e3d6000fd5b505050506040513d60208110156119e057600080fd5b5051835184906001858802019081106119f557fe5b602002602001018181525050505b600101611862565b50949350505050565b60606000600690506060818451026001600160401b0381118015611a3757600080fd5b50604051908082528060200260200182016040528015611a61578160200160208202803683370190505b50905060005b8451811015611a0b576000858281518110611a7e57fe5b602090810291909101810151604080516317e1d38560e11b81526001600160a01b0380841660048301526001602483018190526044830152600060648301529151929450908a1692632fc3a70a92608480840193829003018186803b158015611ae657600080fd5b505afa158015611afa573d6000803e3d6000fd5b505050506040513d6020811015611b1057600080fd5b505183518490848702908110611b2257fe5b602090810291909101810191909152604080516317e1d38560e11b81526001600160a01b0384811660048301526000602483018190526001604484015260648301529151918a1692632fc3a70a92608480840193829003018186803b158015611b8a57600080fd5b505afa158015611b9e573d6000803e3d6000fd5b505050506040513d6020811015611bb457600080fd5b505183518490600185880201908110611bc957fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600160248301529151918a16926356c8c2c192604480840193829003018186803b158015611c2357600080fd5b505afa158015611c37573d6000803e3d6000fd5b505050506040513d6020811015611c4d57600080fd5b505183518490600285880201908110611c6257fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600060248301529151918a16926356c8c2c192604480840193829003018186803b158015611cbc57600080fd5b505afa158015611cd0573d6000803e3d6000fd5b505050506040513d6020811015611ce657600080fd5b505183518490600385880201908110611cfb57fe5b602002602001018181525050866001600160a01b0316636ce8a44b826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611d5457600080fd5b505afa158015611d68573d6000803e3d6000fd5b505050506040513d6020811015611d7e57600080fd5b5051611d8b576000611d8e565b60015b60ff168385840260040181518110611da257fe5b602002602001018181525050866001600160a01b03166348cac277826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611dfb57600080fd5b505afa158015611e0f573d6000803e3d6000fd5b505050506040513d6020811015611e2557600080fd5b505183518490600585880201908110611e3a57fe5b602090810291909101015250600101611a67565b600080600080876001600160a01b03166381a612d6886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d6020811015611ecb57600080fd5b5051604080516323b95ceb60e21b81526001600160a01b038a811660048301529151929350600092918b1691638ee573ac91602480820192602092909190829003018186803b158015611f1d57600080fd5b505afa158015611f31573d6000803e3d6000fd5b505050506040513d6020811015611f4757600080fd5b505190506000611f6d68327cb2734119d3b7a9601e1b611f6789866157bc565b90615815565b9050611f89600a83900a611f6783670de0b6b3a76400006157bc565b905060008a6001600160a01b03166342b60b038b6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611fda57600080fd5b505afa158015611fee573d6000803e3d6000fd5b505050506040513d602081101561200457600080fd5b5051801561208757508a6001600160a01b03166342b60b038a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561205a57600080fd5b505afa15801561206e573d6000803e3d6000fd5b505050506040513d602081101561208457600080fd5b50515b90506000816120fa578b6001600160a01b031663a22f23926040518163ffffffff1660e01b815260040160206040518083038186803b1580156120c957600080fd5b505afa1580156120dd573d6000803e3d6000fd5b505050506040513d60208110156120f357600080fd5b5051612160565b8b6001600160a01b031663df73a2676040518163ffffffff1660e01b815260040160206040518083038186803b15801561213357600080fd5b505afa158015612147573d6000803e3d6000fd5b505050506040513d602081101561215d57600080fd5b50515b90506000826121d3578c6001600160a01b0316637a210a2b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156121a257600080fd5b505afa1580156121b6573d6000803e3d6000fd5b505050506040513d60208110156121cc57600080fd5b5051612239565b8c6001600160a01b03166310eb56c26040518163ffffffff1660e01b815260040160206040518083038186803b15801561220c57600080fd5b505afa158015612220573d6000803e3d6000fd5b505050506040513d602081101561223657600080fd5b50515b905060008d6001600160a01b031663c7e074c38e87868660016040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b1580156122ad57600080fd5b505afa1580156122c1573d6000803e3d6000fd5b505050506040513d60208110156122d757600080fd5b8101908080519060200190929190505050905060008e6001600160a01b031663c7e074c38e88878760006040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b15801561235c57600080fd5b505afa158015612370573d6000803e3d6000fd5b505050506040513d602081101561238657600080fd5b505190506000818311612399578161239b565b825b9b509199509750505050505050509450945094915050565b60606000600790506060818451026001600160401b03811180156123d657600080fd5b50604051908082528060200260200182016040528015612400578160200160208202803683370190505b50905060005b8451811015611a0b57600085828151811061241d57fe5b60200260200101519050806001600160a01b0316635d50e729886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561247457600080fd5b505afa158015612488573d6000803e3d6000fd5b505050506040513d602081101561249e57600080fd5b5051835184908487029081106124b057fe5b602002602001018181525050806001600160a01b031663d5a73fdd886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561250957600080fd5b505afa15801561251d573d6000803e3d6000fd5b505050506040513d602081101561253357600080fd5b50518351849060018588020190811061254857fe5b60200260200101818152505085828151811061256057fe5b60200260200101516001600160a01b03166370a08231886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156125b457600080fd5b505afa1580156125c8573d6000803e3d6000fd5b505050506040513d60208110156125de57600080fd5b5051835184906002858802019081106125f357fe5b602002602001018181525050806001600160a01b03166371417b32886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561264c57600080fd5b505afa158015612660573d6000803e3d6000fd5b505050506040513d602081101561267657600080fd5b50518351849060038588020190811061268b57fe5b602002602001018181525050806001600160a01b031663402914f5886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156126e457600080fd5b505afa1580156126f8573d6000803e3d6000fd5b505050506040513d602081101561270e57600080fd5b50518351849060048588020190811061272357fe5b602002602001018181525050806001600160a01b03166308f26c76886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561277c57600080fd5b505afa158015612790573d6000803e3d6000fd5b505050506040513d60208110156127a657600080fd5b5051835184906005858802019081106127bb57fe5b602002602001018181525050806001600160a01b03166345f01ee6886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561281457600080fd5b505afa158015612828573d6000803e3d6000fd5b505050506040513d602081101561283e57600080fd5b50518351849060068588020190811061285357fe5b602090810291909101015250600101612406565b60606000600c905060008690506000816001600160a01b031663741bef1a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156128af57600080fd5b505afa1580156128c3573d6000803e3d6000fd5b505050506040513d60208110156128d957600080fd5b5051855190915060609084026001600160401b03811180156128fa57600080fd5b50604051908082528060200260200182016040528015612924578160200160208202803683370190505b50905060005b86518110156115de57600087828151811061294157fe5b6020026020010151905060006001600160a01b0316816001600160a01b031614156129695750885b846001600160a01b03166352f55eed826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156129b657600080fd5b505afa1580156129ca573d6000803e3d6000fd5b505050506040513d60208110156129e057600080fd5b5051835184908489029081106129f257fe5b602002602001018181525050846001600160a01b031663c3c7b9e9826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612a4b57600080fd5b505afa158015612a5f573d6000803e3d6000fd5b505050506040513d6020811015612a7557600080fd5b5051835184906001858a0201908110612a8a57fe5b602002602001018181525050846001600160a01b0316631aa4ace5826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612ae357600080fd5b505afa158015612af7573d6000803e3d6000fd5b505050506040513d6020811015612b0d57600080fd5b5051835184906002858a0201908110612b2257fe5b602002602001018181525050846001600160a01b0316632c668ec1828b6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b158015612b8357600080fd5b505afa158015612b97573d6000803e3d6000fd5b505050506040513d6020811015612bad57600080fd5b5051835184906003858a0201908110612bc257fe5b602002602001018181525050846001600160a01b031663ab2f3ad4826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612c1b57600080fd5b505afa158015612c2f573d6000803e3d6000fd5b505050506040513d6020811015612c4557600080fd5b5051835184906004858a0201908110612c5a57fe5b602002602001018181525050846001600160a01b0316634a993ee9826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612cb357600080fd5b505afa158015612cc7573d6000803e3d6000fd5b505050506040513d6020811015612cdd57600080fd5b5051835184906005858a0201908110612cf257fe5b602002602001018181525050846001600160a01b031663ad1e4f8d826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612d4b57600080fd5b505afa158015612d5f573d6000803e3d6000fd5b505050506040513d6020811015612d7557600080fd5b5051835184906006858a0201908110612d8a57fe5b602002602001018181525050846001600160a01b03166381a612d6826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612de357600080fd5b505afa158015612df7573d6000803e3d6000fd5b505050506040513d6020811015612e0d57600080fd5b5051835184906007858a0201908110612e2257fe5b602002602001018181525050846001600160a01b031663e124e6d2826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612e7b57600080fd5b505afa158015612e8f573d6000803e3d6000fd5b505050506040513d6020811015612ea557600080fd5b5051835184906008858a0201908110612eba57fe5b602002602001018181525050846001600160a01b031663f07456ce826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612f1357600080fd5b505afa158015612f27573d6000803e3d6000fd5b505050506040513d6020811015612f3d57600080fd5b5051835184906009858a0201908110612f5257fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600060248301529151918716926356c8c2c192604480840193829003018186803b158015612fac57600080fd5b505afa158015612fc0573d6000803e3d6000fd5b505050506040513d6020811015612fd657600080fd5b505183518490600a858a0201908110612feb57fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600160248301529151918716926356c8c2c192604480840193829003018186803b15801561304557600080fd5b505afa158015613059573d6000803e3d6000fd5b505050506040513d602081101561306f57600080fd5b505183518490600b858a020190811061308457fe5b60209081029190910101525060010161292a565b60608082516001600160401b03811180156130b257600080fd5b506040519080825280602002602001820160405280156130dc578160200160208202803683370190505b50905060005b835181101561180557846001600160a01b0316631ce9cb8f85838151811061310657fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561314b57600080fd5b505afa15801561315f573d6000803e3d6000fd5b505050506040513d602081101561317557600080fd5b5051825183908390811061318557fe5b60209081029190910101526001016130e2565b601281565b60606000600e905060008690506000816001600160a01b031663741bef1a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156131e557600080fd5b505afa1580156131f9573d6000803e3d6000fd5b505050506040513d602081101561320f57600080fd5b5051855190915060609084026001600160401b038111801561323057600080fd5b5060405190808252806020026020018201604052801561325a578160200160208202803683370190505b50905060005b86518110156115de57600087828151811061327757fe5b6020026020010151905060006001600160a01b0316816001600160a01b0316141561329f5750885b60008054600160a01b900460ff166132b8576000613332565b856001600160a01b0316639698d25a836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561330557600080fd5b505afa158015613319573d6000803e3d6000fd5b505050506040513d602081101561332f57600080fd5b50515b9050856001600160a01b03166352f55eed836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561338157600080fd5b505afa158015613395573d6000803e3d6000fd5b505050506040513d60208110156133ab57600080fd5b505184518590858a029081106133bd57fe5b602002602001018181525050856001600160a01b031663c3c7b9e9836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561341657600080fd5b505afa15801561342a573d6000803e3d6000fd5b505050506040513d602081101561344057600080fd5b5051845185906001868b020190811061345557fe5b602002602001018181525050856001600160a01b0316631aa4ace5836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d60208110156134d857600080fd5b5051845185906002868b02019081106134ed57fe5b602002602001018181525050856001600160a01b0316632c668ec1838c6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b15801561354e57600080fd5b505afa158015613562573d6000803e3d6000fd5b505050506040513d602081101561357857600080fd5b5051845185906003868b020190811061358d57fe5b602002602001018181525050856001600160a01b031663ab2f3ad4836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135e657600080fd5b505afa1580156135fa573d6000803e3d6000fd5b505050506040513d602081101561361057600080fd5b5051845185906004868b020190811061362557fe5b602002602001018181525050856001600160a01b0316634a993ee9836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561367e57600080fd5b505afa158015613692573d6000803e3d6000fd5b505050506040513d60208110156136a857600080fd5b5051845185906005868b02019081106136bd57fe5b602002602001018181525050856001600160a01b031663ad1e4f8d836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561371657600080fd5b505afa15801561372a573d6000803e3d6000fd5b505050506040513d602081101561374057600080fd5b5051845185906006868b020190811061375557fe5b602002602001018181525050856001600160a01b0316638a78daa8836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156137ae57600080fd5b505afa1580156137c2573d6000803e3d6000fd5b505050506040513d60208110156137d857600080fd5b5051845185906007868b02019081106137ed57fe5b60200260200101818152505080848885026008018151811061380b57fe5b602002602001018181525050856001600160a01b03166381a612d6836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561386457600080fd5b505afa158015613878573d6000803e3d6000fd5b505050506040513d602081101561388e57600080fd5b5051845185906009868b02019081106138a357fe5b602002602001018181525050856001600160a01b031663e124e6d2836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156138fc57600080fd5b505afa158015613910573d6000803e3d6000fd5b505050506040513d602081101561392657600080fd5b505184518590600a868b020190811061393b57fe5b602002602001018181525050856001600160a01b031663f07456ce836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561399457600080fd5b505afa1580156139a8573d6000803e3d6000fd5b505050506040513d60208110156139be57600080fd5b505184518590600b868b02019081106139d357fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038581166004830152600060248301529151918816926356c8c2c192604480840193829003018186803b158015613a2d57600080fd5b505afa158015613a41573d6000803e3d6000fd5b505050506040513d6020811015613a5757600080fd5b505184518590600c868b0201908110613a6c57fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038581166004830152600160248301529151918816926356c8c2c192604480840193829003018186803b158015613ac657600080fd5b505afa158015613ada573d6000803e3d6000fd5b505050506040513d6020811015613af057600080fd5b505184518590600d868b0201908110613b0557fe5b60209081029190910101525050600101613260565b60606000600290506060818451026001600160401b0381118015613b3d57600080fd5b50604051908082528060200260200182016040528015613b67578160200160208202803683370190505b50905060005b8451811015611a0b576000858281518110613b8457fe5b60200260200101519050806001600160a01b031663402914f5886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613bdb57600080fd5b505afa158015613bef573d6000803e3d6000fd5b505050506040513d6020811015613c0557600080fd5b505183518490848702908110613c1757fe5b602002602001018181525050806001600160a01b0316632459f51d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613c5c57600080fd5b505afa158015613c70573d6000803e3d6000fd5b505050506040513d6020811015613c8657600080fd5b505183518490600185880201908110613c9b57fe5b602090810291909101015250600101613b6d565b68327cb2734119d3b7a9601e1b81565b60606000600290506060818451026001600160401b0381118015613ce257600080fd5b50604051908082528060200260200182016040528015613d0c578160200160208202803683370190505b5090508560005b855181101561417f576000868281518110613d2a57fe5b6020026020010151905060006001600160a01b0316816001600160a01b03161415613d525750865b6000836001600160a01b03166342b60b03836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613da157600080fd5b505afa158015613db5573d6000803e3d6000fd5b505050506040513d6020811015613dcb57600080fd5b5051613e3b57836001600160a01b031663c4f718bf6040518163ffffffff1660e01b815260040160206040518083038186803b158015613e0a57600080fd5b505afa158015613e1e573d6000803e3d6000fd5b505050506040513d6020811015613e3457600080fd5b5051613ea1565b836001600160a01b031663134ca63b6040518163ffffffff1660e01b815260040160206040518083038186803b158015613e7457600080fd5b505afa158015613e88573d6000803e3d6000fd5b505050506040513d6020811015613e9e57600080fd5b50515b90506000846001600160a01b031663c3c7b9e9846040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613ef257600080fd5b505afa158015613f06573d6000803e3d6000fd5b505050506040513d6020811015613f1c57600080fd5b5051604080516352f55eed60e01b81526001600160a01b0386811660048301529151929350600092918816916352f55eed91602480820192602092909190829003018186803b158015613f6e57600080fd5b505afa158015613f82573d6000803e3d6000fd5b505050506040513d6020811015613f9857600080fd5b505190508015613fcb57613fb081611f6785856157bc565b8789870281518110613fbe57fe5b6020026020010181815250505b6000866001600160a01b031663c65bc7b1866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561401a57600080fd5b505afa15801561402e573d6000803e3d6000fd5b505050506040513d602081101561404457600080fd5b5051111561416f576000866001600160a01b031663a93acac2866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561409b57600080fd5b505afa1580156140af573d6000803e3d6000fd5b505050506040513d60208110156140c557600080fd5b50516040805163c65bc7b160e01b81526001600160a01b0388811660048301529151929350600092918a169163c65bc7b191602480820192602092909190829003018186803b15801561411757600080fd5b505afa15801561412b573d6000803e3d6000fd5b505050506040513d602081101561414157600080fd5b5051905061414f8183615854565b898b89026001018151811061416057fe5b60200260200101818152505050505b505060019092019150613d139050565b5090925050505b9392505050565b6000546001600160a01b031633146141e4576040805162461bcd60e51b815260206004820152601560248201527423b7bb32b93730b136329d103337b93134b23232b760591b604482015290519081900360640190fd5b60008054911515600160a01b0260ff60a01b19909216919091179055565b80516060906002908190839082900482026001600160401b038111801561422857600080fd5b50604051908082528060200260200182016040528015614252578160200160208202803683370190505b50905060005b8386518161426257fe5b04811015614464576000868583028151811061427a57fe5b602002602001015190506000878684026001018151811061429757fe5b602002602001015190506000896001600160a01b031663e6a4390584846040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b15801561430157600080fd5b505afa158015614315573d6000803e3d6000fd5b505050506040513d602081101561432b57600080fd5b5051604080516370a0823160e01b81526001600160a01b0380841660048301529151929350908516916370a0823191602480820192602092909190829003018186803b15801561437a57600080fd5b505afa15801561438e573d6000803e3d6000fd5b505050506040513d60208110156143a457600080fd5b5051855186908689029081106143b657fe5b602002602001018181525050816001600160a01b03166370a08231826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561440f57600080fd5b505afa158015614423573d6000803e3d6000fd5b505050506040513d602081101561443957600080fd5b5051855186906001878a020190811061444e57fe5b6020908102919091010152505050600101614258565b5095945050505050565b600981565b600054600160a01b900460ff1681565b6000546001600160a01b031633146144da576040805162461bcd60e51b815260206004820152601560248201527423b7bb32b93730b136329d103337b93134b23232b760591b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000866001600160a01b03166381a612d6876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561454e57600080fd5b505afa158015614562573d6000803e3d6000fd5b505050506040513d602081101561457857600080fd5b5051604080516323b95ceb60e21b81526001600160a01b0389811660048301529151929350600092918a1691638ee573ac91602480820192602092909190829003018186803b1580156145ca57600080fd5b505afa1580156145de573d6000803e3d6000fd5b505050506040513d60208110156145f457600080fd5b5051604080516323b95ceb60e21b81526001600160a01b0389811660048301529151929350600092918b1691638ee573ac91602480820192602092909190829003018186803b15801561464657600080fd5b505afa15801561465a573d6000803e3d6000fd5b505050506040513d602081101561467057600080fd5b5051905060008061469168327cb2734119d3b7a9601e1b611f678a886157bc565b90506146ad600a85900a611f6783670de0b6b3a76400006157bc565b905060008b6001600160a01b03166342b60b038c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156146fe57600080fd5b505afa158015614712573d6000803e3d6000fd5b505050506040513d602081101561472857600080fd5b505180156147ab57508b6001600160a01b03166342b60b038b6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561477e57600080fd5b505afa158015614792573d6000803e3d6000fd5b505050506040513d60208110156147a857600080fd5b50515b905060008161481e578c6001600160a01b031663a22f23926040518163ffffffff1660e01b815260040160206040518083038186803b1580156147ed57600080fd5b505afa158015614801573d6000803e3d6000fd5b505050506040513d602081101561481757600080fd5b5051614884565b8c6001600160a01b031663df73a2676040518163ffffffff1660e01b815260040160206040518083038186803b15801561485757600080fd5b505afa15801561486b573d6000803e3d6000fd5b505050506040513d602081101561488157600080fd5b50515b90506000826148f7578d6001600160a01b0316637a210a2b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156148c657600080fd5b505afa1580156148da573d6000803e3d6000fd5b505050506040513d60208110156148f057600080fd5b505161495d565b8d6001600160a01b03166310eb56c26040518163ffffffff1660e01b815260040160206040518083038186803b15801561493057600080fd5b505afa158015614944573d6000803e3d6000fd5b505050506040513d602081101561495a57600080fd5b50515b905060008e6001600160a01b031663c7e074c38f87868660016040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b1580156149d157600080fd5b505afa1580156149e5573d6000803e3d6000fd5b505050506040513d60208110156149fb57600080fd5b8101908080519060200190929190505050905060008f6001600160a01b031663c7e074c38f88878760006040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b158015614a8057600080fd5b505afa158015614a94573d6000803e3d6000fd5b505050506040513d6020811015614aaa57600080fd5b50519050808211614abb5780614abd565b815b965050505050505060008a6001600160a01b031663e124e6d28a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015614b1457600080fd5b505afa158015614b28573d6000803e3d6000fd5b505050506040513d6020811015614b3e57600080fd5b505190506000614b5282611f678b896157bc565b9050614b7185600a0a611f6786600a0a846157bc90919063ffffffff16565b90506000614b8f612710611f67614b88828861577a565b85906157bc565b90506000614b9d838361577a565b919e919d50909b505050505050505050505050565b60608082516001600160401b0381118015614bcc57600080fd5b50604051908082528060200260200182016040528015614bf6578160200160208202803683370190505b50905060005b8351811015611805576000848281518110614c1357fe5b6020026020010151905060006001600160a01b0316816001600160a01b03161415614c6157856001600160a01b031631838381518110614c4f57fe5b60200260200101818152505050614cf6565b806001600160a01b03166370a08231876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015614cae57600080fd5b505afa158015614cc2573d6000803e3d6000fd5b505050506040513d6020811015614cd857600080fd5b50518351849084908110614ce857fe5b602002602001018181525050505b600101614bfc565b60608060098551026001600160401b0381118015614d1b57600080fd5b50604051908082528060200260200182016040528015614d45578160200160208202803683370190505b50905060005b85518110156150ef5760008060008060008060008e6001600160a01b0316634a3f088d8f8f8b81518110614d7b57fe5b60200260200101518f8c81518110614d8f57fe5b60200260200101518f8d81518110614da357fe5b60200260200101516040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b0316815260200182151581526020019450505050506101006040518083038186803b158015614e1257600080fd5b505afa158015614e26573d6000803e3d6000fd5b505050506040513d610100811015614e3d57600080fd5b50805160208201516040830151606084015160a085015160c086015160e0909601518f51959d50939b509199509750955091935090915087908a9060098b02908110614e8557fe5b602002602001018181525050858960098a0260010181518110614ea457fe5b602002602001018181525050848960098a0260020181518110614ec357fe5b602002602001018181525050838960098a0260030181518110614ee257fe5b60200260200101818152505081614efa576000614efd565b60015b60ff168960098a0260040181518110614f1257fe5b602002602001018181525050828960098a0260050181518110614f3157fe5b602002602001018181525050808960098a0260060181518110614f5057fe5b602002602001018181525050505050505050506000826009830281518110614f7457fe5b602002602001015190506000836009840260020181518110614f9257fe5b602002602001015190506000846009850260060181518110614fb057fe5b6020026020010151905060008211156150e4576000808c6001600160a01b0316635c07eaab8b8881518110614fe157fe5b602002602001015187878d8b81518110614ff757fe5b6020026020010151886040518663ffffffff1660e01b815260040180866001600160a01b03168152602001858152602001848152602001831515815260200182815260200195505050505050604080518083038186803b15801561505a57600080fd5b505afa15801561506e573d6000803e3d6000fd5b505050506040513d604081101561508457600080fd5b50805160209091015190925090508161509e5760006150a1565b60015b60ff168760098802600701815181106150b657fe5b602002602001018181525050808760098802600801815181106150d557fe5b60200260200101818152505050505b505050600101614d4b565b509695505050505050565b600080846001600160a01b03166381a612d6856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561514a57600080fd5b505afa15801561515e573d6000803e3d6000fd5b505050506040513d602081101561517457600080fd5b505160408051637092736960e11b81526001600160a01b03868116600483015291519293506000929188169163e124e6d291602480820192602092909190829003018186803b1580156151c657600080fd5b505afa1580156151da573d6000803e3d6000fd5b505050506040513d60208110156151f057600080fd5b5051604080516323b95ceb60e21b81526001600160a01b038881166004830152915192935060009291891691638ee573ac91602480820192602092909190829003018186803b15801561524257600080fd5b505afa158015615256573d6000803e3d6000fd5b505050506040513d602081101561526c57600080fd5b5051604080516323b95ceb60e21b81526001600160a01b0388811660048301529151929350600092918a1691638ee573ac91602480820192602092909190829003018186803b1580156152be57600080fd5b505afa1580156152d2573d6000803e3d6000fd5b505050506040513d60208110156152e857600080fd5b5051604080516352f55eed60e01b81526001600160a01b038981166004830152915192935060009283928c16916352f55eed916024808301926020929190829003018186803b15801561533a57600080fd5b505afa15801561534e573d6000803e3d6000fd5b505050506040513d602081101561536457600080fd5b50516040805163c3c7b9e960e01b81526001600160a01b038b811660048301529151929350600092918d169163c3c7b9e991602480820192602092909190829003018186803b1580156153b657600080fd5b505afa1580156153ca573d6000803e3d6000fd5b505050506040513d60208110156153e057600080fd5b505160408051634a993ee960e01b81526001600160a01b038c811660048301529151929350600092918e1691634a993ee991602480820192602092909190829003018186803b15801561543257600080fd5b505afa158015615446573d6000803e3d6000fd5b505050506040513d602081101561545c57600080fd5b50519050600081831161546f5781615471565b825b905083811061548c5760009950505050505050505050614186565b6000615498858361577a565b90506154c587600a0a611f678a600a0a6154bf8e611f678f886157bc90919063ffffffff16565b906157bc565b955050505050506000896001600160a01b031663ad1e4f8d8a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561551b57600080fd5b505afa15801561552f573d6000803e3d6000fd5b505050506040513d602081101561554557600080fd5b5051905080156115de57896001600160a01b0316631aa4ace58a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561559c57600080fd5b505afa1580156155b0573d6000803e3d6000fd5b505050506040513d60208110156155c657600080fd5b50518110156155de5760009650505050505050614186565b60006156638b6001600160a01b0316631aa4ace58c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561563057600080fd5b505afa158015615644573d6000803e3d6000fd5b505050506040513d602081101561565a57600080fd5b5051839061577a565b905061567f670de0b6b3a7640000611f6783600a89900a6157bc565b905061569b87611f678368327cb2734119d3b7a9601e1b6157bc565b9050808311156156b357965061418695505050505050565b505098975050505050505050565b600080805b83518110156118055760008482815181106156dd57fe5b602002602001015190506000866001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561573657600080fd5b505afa15801561574a573d6000803e3d6000fd5b505050506040513d602081101561576057600080fd5b5051905061576e8482615854565b935050506001016156c6565b600061418683836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506158ac565b6000826157cb57506000611809565b828202828482816157d857fe5b04146141865760405162461bcd60e51b81526004018080602001828103825260218152602001806159a96021913960400191505060405180910390fd5b600061418683836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250615943565b600082820183811015614186576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6000818484111561593b5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156159005781810151838201526020016158e8565b50505050905090810190601f16801561592d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600081836159925760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156159005781810151838201526020016158e8565b50600083858161599e57fe5b049594505050505056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122045b628c599a8154fc42d4309c34d7a311429d723c48990eab8da614a960534d364736f6c634300060c0033 \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/Router.abi b/src/Managing.Tools.ABI/Gmx/core/Router.abi new file mode 100644 index 0000000..16f6965 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/Router.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_usdg","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Swap","type":"event"},{"inputs":[{"internalType":"address","name":"_plugin","type":"address"}],"name":"addPlugin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_plugin","type":"address"}],"name":"approvePlugin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"approvedPlugins","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"decreasePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"}],"name":"decreasePositionAndSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address payable","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"}],"name":"decreasePositionAndSwapETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address payable","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"decreasePositionETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_plugin","type":"address"}],"name":"denyPlugin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"directPoolDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"increasePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"increasePositionETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"pluginDecreasePosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"pluginIncreasePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"pluginTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"plugins","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_plugin","type":"address"}],"name":"removePlugin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"swapETHToTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"address payable","name":"_receiver","type":"address"}],"name":"swapTokensToETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdg","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Gmx/core/Router.bin b/src/Managing.Tools.ABI/Gmx/core/Router.bin new file mode 100644 index 0000000..f55e113 --- /dev/null +++ b/src/Managing.Tools.ABI/Gmx/core/Router.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506040516124773803806124778339818101604052606081101561003357600080fd5b5080516020820151604090920151600380546001600160a01b039384166001600160a01b0319918216179091556002805494841694821694909417909355600180549290911691831691909117905560008054909116331790556123db8061009c6000396000f3fe60806040526004361061011f5760003560e01c806312d43a51146101835780631b827878146101b45780631f1dd176146101fd5780632662166b1461024e5780632d4ba6a7146102c15780633039e37f1461038357806338c74dd9146104635780633fc8cef314610496578063430ed37c146104ab5780634b12e6431461050a5780635fc8500e146105515780636023e9661461063157806390205d8c146106f357806390b64ad314610752578063956f285e1461078b578063a4d95b64146107c6578063abe68eaa146107f9578063b32755de146108a8578063b7ddc9921461096b578063cedd437514610a41578063cfad57a214610a74578063d8867fc814610aa7578063f5b91b7b14610ada578063fbfa77cf14610aef5761017e565b3661017e576001546001600160a01b0316331461017c576040805162461bcd60e51b81526020600482015260166024820152752937baba32b91d1034b73b30b634b21039b2b73232b960511b604482015290519081900360640190fd5b005b600080fd5b34801561018f57600080fd5b50610198610b04565b604080516001600160a01b039092168252519081900360200190f35b3480156101c057600080fd5b5061017c600480360360808110156101d757600080fd5b506001600160a01b03813581169160208101358216916040820135169060600135610b13565b34801561020957600080fd5b5061017c600480360360a081101561022057600080fd5b506001600160a01b038135811691602081013582169160408201351690606081013590608001351515610b37565b34801561025a57600080fd5b506102af600480360360e081101561027157600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359160808201359160a081013515159160c09091013516610bcc565b60408051918252519081900360200190f35b3480156102cd57600080fd5b5061017c600480360360808110156102e457600080fd5b810190602081018135600160201b8111156102fe57600080fd5b82018360208201111561031057600080fd5b803590602001918460208302840111600160201b8311171561033157600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602081013590604001356001600160a01b0316610c8c565b34801561038f57600080fd5b5061017c60048036036101008110156103a757600080fd5b810190602081018135600160201b8111156103c157600080fd5b8201836020820111156103d357600080fd5b803590602001918460208302840111600160201b831117156103f457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b0390811693506020830135926040810135925060608101351515916080820135169060a08101359060c00135610df3565b34801561046f57600080fd5b5061017c6004803603602081101561048657600080fd5b50356001600160a01b0316610ef8565b3480156104a257600080fd5b50610198610f29565b3480156104b757600080fd5b5061017c600480360360e08110156104ce57600080fd5b506001600160a01b0381358116916020810135821691604082013591606081013591608082013515159160a08101359091169060c00135610f38565b34801561051657600080fd5b5061053d6004803603602081101561052d57600080fd5b50356001600160a01b0316610f5f565b604080519115158252519081900360200190f35b34801561055d57600080fd5b5061017c600480360361010081101561057557600080fd5b810190602081018135600160201b81111561058f57600080fd5b8201836020820111156105a157600080fd5b803590602001918460208302840111600160201b831117156105c257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b0390811693506020830135926040810135925060608101351515916080820135169060a08101359060c00135610f74565b34801561063d57600080fd5b5061017c6004803603608081101561065457600080fd5b810190602081018135600160201b81111561066e57600080fd5b82018360208201111561068057600080fd5b803590602001918460208302840111600160201b831117156106a157600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602081013590604001356001600160a01b0316610fba565b3480156106ff57600080fd5b5061017c600480360360e081101561071657600080fd5b506001600160a01b0381358116916020810135821691604082013591606081013591608082013515159160a08101359091169060c00135610ff1565b34801561075e57600080fd5b5061017c6004803603604081101561077557600080fd5b506001600160a01b038135169060200135611000565b34801561079757600080fd5b5061053d600480360360408110156107ae57600080fd5b506001600160a01b038135811691602001351661108c565b3480156107d257600080fd5b5061017c600480360360208110156107e957600080fd5b50356001600160a01b03166110ac565b61017c6004803603606081101561080f57600080fd5b810190602081018135600160201b81111561082957600080fd5b82018360208201111561083b57600080fd5b803590602001918460208302840111600160201b8311171561085c57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602001356001600160a01b0316611120565b61017c600480360360c08110156108be57600080fd5b810190602081018135600160201b8111156108d857600080fd5b8201836020820111156108ea57600080fd5b803590602001918460208302840111600160201b8311171561090b57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550506001600160a01b0383351693505050602081013590604081013590606081013515159060800135611235565b34801561097757600080fd5b5061017c600480360360e081101561098e57600080fd5b810190602081018135600160201b8111156109a857600080fd5b8201836020820111156109ba57600080fd5b803590602001918460208302840111600160201b831117156109db57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550506001600160a01b0383351693505050602081013590604081013590606081013590608081013515159060a00135611325565b348015610a4d57600080fd5b5061017c60048036036020811015610a6457600080fd5b50356001600160a01b03166113d2565b348015610a8057600080fd5b5061017c60048036036020811015610a9757600080fd5b50356001600160a01b0316611400565b348015610ab357600080fd5b5061017c60048036036020811015610aca57600080fd5b50356001600160a01b0316611475565b348015610ae657600080fd5b506101986114ec565b348015610afb57600080fd5b506101986114fb565b6000546001600160a01b031681565b610b1c8361150a565b610b316001600160a01b0385168484846115e0565b50505050565b610b408561150a565b600354604080516348d91abf60e01b81526001600160a01b03888116600483015287811660248301528681166044830152606482018690528415156084830152915191909216916348d91abf9160a480830192600092919082900301818387803b158015610bad57600080fd5b505af1158015610bc1573d6000803e3d6000fd5b505050505050505050565b6000610bd78861150a565b6003546040805163082a084960e41b81526001600160a01b038b811660048301528a811660248301528981166044830152606482018990526084820188905286151560a483015285811660c4830152915191909216916382a084909160e48083019260209291908290030181600087803b158015610c5457600080fd5b505af1158015610c68573d6000803e3d6000fd5b505050506040513d6020811015610c7e57600080fd5b505198975050505050505050565b60015484516001600160a01b039091169085906000198101908110610cad57fe5b60200260200101516001600160a01b031614610cfe576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b610d48610d0961163a565b60035486516001600160a01b039091169086908890600090610d2757fe5b60200260200101516001600160a01b03166115e0909392919063ffffffff16565b6000610d5585843061163e565b9050610d61818361177e565b6000805160206123388339815191523386600081518110610d7e57fe5b602002602001015187600189510381518110610d9657fe5b6020026020010151878560405180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019550505050505060405180910390a15050505050565b60015488516001600160a01b039091169089906000198101908110610e1457fe5b60200260200101516001600160a01b031614610e65576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b6000610e8a89600081518110610e7757fe5b60200260200101518989898930896117fb565b9050610ed3600360009054906101000a90046001600160a01b0316828b600081518110610eb357fe5b60200260200101516001600160a01b0316611a089092919063ffffffff16565b6000610ee08a843061163e565b9050610eec818661177e565b50505050505050505050565b3360009081526005602090815260408083206001600160a01b0394909416835292905220805460ff19166001179055565b6001546001600160a01b031681565b6000610f49888888888830886117fb565b9050610f55818461177e565b5050505050505050565b60046020526000908152604090205460ff1681565b6000610f8689600081518110610e7757fe5b9050610faf600360009054906101000a90046001600160a01b0316828b600081518110610eb357fe5b610eec89838661163e565b610fc5610d0961163a565b6000610fd285848461163e565b90506000805160206123388339815191523386600081518110610d7e57fe5b610f55878787878787876117fb565b61102261100b61163a565b6003546001600160a01b03858116929116846115e0565b60035460408051635f7bc11960e01b81526001600160a01b03858116600483015291519190921691635f7bc11991602480830192600092919082900301818387803b15801561107057600080fd5b505af1158015611084573d6000803e3d6000fd5b505050505050565b600560209081526000928352604080842090915290825290205460ff1681565b6000546001600160a01b031633146110ff576040805162461bcd60e51b81526020600482015260116024820152702937baba32b91d103337b93134b23232b760791b604482015290519081900360640190fd5b6001600160a01b03166000908152600460205260409020805460ff19169055565b60015483516001600160a01b0390911690849060009061113c57fe5b60200260200101516001600160a01b03161461118d576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b611195611a5f565b60006111a284848461163e565b905060008051602061233883398151915233856000815181106111c157fe5b6020026020010151866001885103815181106111d957fe5b6020026020010151348560405180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019550505050505060405180910390a150505050565b60015486516001600160a01b0390911690879060009061125157fe5b60200260200101516001600160a01b0316146112a2576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b34156112b0576112b0611a5f565b600186511180156112c15750600034115b156113015760006112d387863061163e565b90506112ff600360009054906101000a90046001600160a01b0316828960018b510381518110610eb357fe5b505b6110848660018851038151811061131457fe5b602002602001015186858585611ae8565b84156113545761135461133661163a565b60035489516001600160a01b039091169088908b90600090610d2757fe5b600187511180156113655750600085115b156113a557600061137788863061163e565b90506113a3600360009054906101000a90046001600160a01b0316828a60018c510381518110610eb357fe5b505b6113c9876001895103815181106113b857fe5b602002602001015187858585611ae8565b50505050505050565b3360009081526005602090815260408083206001600160a01b0394909416835292905220805460ff19169055565b6000546001600160a01b03163314611453576040805162461bcd60e51b81526020600482015260116024820152702937baba32b91d103337b93134b23232b760791b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b031633146114c8576040805162461bcd60e51b81526020600482015260116024820152702937baba32b91d103337b93134b23232b760791b604482015290519081900360640190fd5b6001600160a01b03166000908152600460205260409020805460ff19166001179055565b6002546001600160a01b031681565b6003546001600160a01b031681565b3360009081526004602052604090205460ff16611567576040805162461bcd60e51b81526020600482015260166024820152752937baba32b91d1034b73b30b634b21038363ab3b4b760511b604482015290519081900360640190fd5b6001600160a01b038116600090815260056020908152604080832033845290915290205460ff166115dd576040805162461bcd60e51b815260206004820152601b60248201527a149bdd5d195c8e881c1b1d59da5b881b9bdd08185c1c1c9bdd9959602a1b604482015290519081900360640190fd5b50565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610b31908590611ce3565b3390565b60008351600214156116855761167e8460008151811061165a57fe5b60200260200101518560018151811061166f57fe5b60200260200101518585611d94565b9050611777565b83516003141561172b5760006116c6856000815181106116a157fe5b6020026020010151866001815181106116b657fe5b6020026020010151600030611d94565b90506116ef600360009054906101000a90046001600160a01b03168287600181518110610eb357fe5b611723856001815181106116ff57fe5b60200260200101518660028151811061171457fe5b60200260200101518686611d94565b915050611777565b6040805162461bcd60e51b815260206004820152601c60248201527b0a4deeae8cae47440d2dcecc2d8d2c840bee0c2e8d05cd8cadccee8d60231b604482015290519081900360640190fd5b9392505050565b60015460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921691632e1a7d4d9160248082019260009290919082900301818387803b1580156117cb57600080fd5b505af11580156117df573d6000803e3d6000fd5b506117f7925050506001600160a01b03821683611f92565b5050565b600083156118c157600354604080516340d3096b60e11b81526001600160a01b038a811660048301529151859392909216916381a612d691602480820192602092909190829003018186803b15801561185357600080fd5b505afa158015611867573d6000803e3d6000fd5b505050506040513d602081101561187d57600080fd5b505110156118bc5760405162461bcd60e51b81526004018080602001828103825260238152602001806123156023913960400191505060405180910390fd5b61197a565b60035460408051637092736960e11b81526001600160a01b038a8116600483015291518593929092169163e124e6d291602480820192602092909190829003018186803b15801561191157600080fd5b505afa158015611925573d6000803e3d6000fd5b505050506040513d602081101561193b57600080fd5b5051111561197a5760405162461bcd60e51b81526004018080602001828103825260248152602001806123826024913960400191505060405180910390fd5b6003546001600160a01b03166382a0849061199361163a565b604080516001600160e01b031960e085901b1681526001600160a01b039283166004820152828d166024820152828c166044820152606481018b9052608481018a905288151560a482015291871660c48301525160e48083019260209291908290030181600087803b158015610c5457600080fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611a5a908490611ce3565b505050565b600160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015611aaf57600080fd5b505af1158015611ac3573d6000803e3d6000fd5b5050600354600154611ae694506001600160a01b03908116935016905034611a08565b565b8115611bac5760035460408051637092736960e11b81526001600160a01b03878116600483015291518493929092169163e124e6d291602480820192602092909190829003018186803b158015611b3e57600080fd5b505afa158015611b52573d6000803e3d6000fd5b505050506040513d6020811015611b6857600080fd5b50511115611ba75760405162461bcd60e51b81526004018080602001828103825260248152602001806123826024913960400191505060405180910390fd5b611c65565b600354604080516340d3096b60e11b81526001600160a01b0387811660048301529151849392909216916381a612d691602480820192602092909190829003018186803b158015611bfc57600080fd5b505afa158015611c10573d6000803e3d6000fd5b505050506040513d6020811015611c2657600080fd5b50511015611c655760405162461bcd60e51b81526004018080602001828103825260238152602001806123156023913960400191505060405180910390fd5b6003546001600160a01b03166348d91abf611c7e61163a565b604080516001600160e01b031960e085901b1681526001600160a01b039283166004820152828a16602482015291881660448301526064820187905285151560848301525160a480830192600092919082900301818387803b158015610bad57600080fd5b6060611d38826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166120779092919063ffffffff16565b805190915015611a5a57808060200190516020811015611d5757600080fd5b5051611a5a5760405162461bcd60e51b815260040180806020018281038252602a815260200180612358602a913960400191505060405180910390fd5b60025460009081906001600160a01b0386811691161415611e39576003546040805163817bb85760e01b81526001600160a01b03898116600483015286811660248301529151919092169163817bb8579160448083019260209291908290030181600087803b158015611e0657600080fd5b505af1158015611e1a573d6000803e3d6000fd5b505050506040513d6020811015611e3057600080fd5b50519050611f34565b6002546001600160a01b0387811691161415611ea65760035460408051630711e61960e41b81526001600160a01b03888116600483015286811660248301529151919092169163711e61909160448083019260209291908290030181600087803b158015611e0657600080fd5b60035460408051634998b10960e11b81526001600160a01b038981166004830152888116602483015286811660448301529151919092169163933162129160648083019260209291908290030181600087803b158015611f0557600080fd5b505af1158015611f19573d6000803e3d6000fd5b505050506040513d6020811015611f2f57600080fd5b505190505b83811015611f89576040805162461bcd60e51b815260206004820152601e60248201527f526f757465723a20696e73756666696369656e7420616d6f756e744f75740000604482015290519081900360640190fd5b95945050505050565b80471015611fe7576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015290519081900360640190fd5b6040516000906001600160a01b0384169083908381818185875af1925050503d8060008114612032576040519150601f19603f3d011682016040523d82523d6000602084013e612037565b606091505b5050905080611a5a5760405162461bcd60e51b815260040180806020018281038252603a8152602001806122b5603a913960400191505060405180910390fd5b6060612086848460008561208e565b949350505050565b6060824710156120cf5760405162461bcd60e51b81526004018080602001828103825260268152602001806122ef6026913960400191505060405180910390fd5b6120d8856121ea565b612129576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106121685780518252601f199092019160209182019101612149565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146121ca576040519150601f19603f3d011682016040523d82523d6000602084013e6121cf565b606091505b50915091506121df8282866121f0565b979650505050505050565b3b151590565b606083156121ff575081611777565b82511561220f5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612259578181015183820152602001612241565b50505050905090810190601f1680156122865780820380516001836020036101000a031916815260200191505b509250505060405180910390fdfe526f757465723a20696e76616c6964205f706174680000000000000000000000416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d61792068617665207265766572746564416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c526f757465723a206d61726b207072696365206c6f776572207468616e206c696d6974cd3829a3813dc3cdd188fd3d01dcf3268c16be2fdd2dd21d0665418816e460625361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564526f757465723a206d61726b20707269636520686967686572207468616e206c696d6974a26469706673582212205fb07c28854d2a79764cfd95b47807b48d7af91fddcab27d57d2e9b67560698464736f6c634300060c0033000000000000000000000000489ee077994b6658eafa855c308275ead8097c4a00000000000000000000000045096e7aa921f27590f8f19e457794eb0967814100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1 \ No newline at end of file diff --git a/src/Managing.Tools.ABI/Managing.Tools.ABI.csproj b/src/Managing.Tools.ABI/Managing.Tools.ABI.csproj new file mode 100644 index 0000000..86c1b1a --- /dev/null +++ b/src/Managing.Tools.ABI/Managing.Tools.ABI.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/src/Managing.Tools.ABI/OrderBook/ContractDefinition/OrderBookDefinition.cs b/src/Managing.Tools.ABI/OrderBook/ContractDefinition/OrderBookDefinition.cs new file mode 100644 index 0000000..8f3543e --- /dev/null +++ b/src/Managing.Tools.ABI/OrderBook/ContractDefinition/OrderBookDefinition.cs @@ -0,0 +1,1171 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Managing.Tools.OrderBook.ContractDefinition +{ + + + public partial class OrderBookDeployment : OrderBookDeploymentBase + { + public OrderBookDeployment() : base(BYTECODE) { } + public OrderBookDeployment(string byteCode) : base(byteCode) { } + } + + public class OrderBookDeploymentBase : ContractDeploymentMessage + { + public static string BYTECODE = "6080604052600e805460ff1916905534801561001a57600080fd5b506001600055600780546001600160a01b031916331790556153c4806100416000396000f3fe6080604052600436106101c35760003560e01c8062cf066b1461022a578063026032ee1461026f57806307c7edc3146102fa5780630d5cc9381461033d57806311d9444a1461036757806312d43a51146103aa578063269ae6c2146103db5780632b7d6290146104a6578063392e53cd146105465780633fc8cef31461056f57806347e0bbd0146105845780634a686d67146105ae5780634c54f0b0146105c357806363ae21031461062d57806379221fa214610642578063807c5600146106c15780638de10c2e1461087357806395082d25146108885780639983ee1b1461089d5780639e23de5c146108db5780639e71b0f01461090e578063a397ea5414610938578063aec224551461097c578063b142a4b0146109af578063c16cde8a14610a99578063c4a1821b14610ae9578063c86b0f7d14610b99578063cfad57a214610bd7578063d0d40cd614610c0a578063d38ab51914610c9d578063d3bab1d114610ce0578063d566d0ca14610d74578063d7c41c7914610da7578063f2d2e01b14610e00578063f5b91b7b14610e94578063f882ac0714610ea9578063f887ea4014610ed3578063fbfa77cf14610ee8578063fc2cee6214610efd57610225565b36610225576008546001600160a01b03163314610223576040805162461bcd60e51b815260206004820152601960248201527827b93232b92137b7b59d1034b73b30b634b21039b2b73232b960391b604482015290519081900360640190fd5b005b600080fd5b34801561023657600080fd5b5061025d6004803603602081101561024d57600080fd5b50356001600160a01b0316610f27565b60408051918252519081900360200190f35b34801561027b57600080fd5b506102a86004803603604081101561029257600080fd5b506001600160a01b038135169060200135610f39565b604080516001600160a01b03998a168152602081019890985295909716868601526060860193909352901515608085015260a0840152151560c083015260e08201929092529051908190036101000190f35b34801561030657600080fd5b506102236004803603606081101561031d57600080fd5b506001600160a01b0381358116916020810135916040909101351661100c565b34801561034957600080fd5b506102236004803603602081101561036057600080fd5b5035611417565b34801561037357600080fd5b506102236004803603606081101561038a57600080fd5b506001600160a01b038135811691602081013591604090910135166114a8565b3480156103b657600080fd5b506103bf611857565b604080516001600160a01b039092168252519081900360200190f35b61022360048036036101008110156103f257600080fd5b810190602081018135600160201b81111561040c57600080fd5b82018360208201111561041e57600080fd5b803590602001918460208302840111600160201b8311171561043f57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602081013590604081013590606081013515159060808101359060a081013515159060c001351515611866565b3480156104b257600080fd5b506104df600480360360408110156104c957600080fd5b506001600160a01b038135169060200135611bef565b604080516001600160a01b039b8c168152998b1660208b015289810198909852958916606089015293909716608087015260a0860191909152151560c085015260e08401949094529215156101008301526101208201929092529051908190036101400190f35b34801561055257600080fd5b5061055b611c61565b604080519115158252519081900360200190f35b34801561057b57600080fd5b506103bf611c6a565b34801561059057600080fd5b50610223600480360360208110156105a757600080fd5b5035611c79565b3480156105ba57600080fd5b5061025d611f83565b3480156105cf57600080fd5b50610614600480360360a08110156105e657600080fd5b5080351515906020810135906001600160a01b03604082013516906060810135151590608001351515611f8f565b6040805192835290151560208301528051918290030190f35b34801561063957600080fd5b5061025d6120f8565b34801561064e57600080fd5b5061067b6004803603604081101561066557600080fd5b506001600160a01b0381351690602001356120fe565b604080516001600160a01b039098168852602088019690965286860194909452606086019290925215156080850152151560a084015260c0830152519081900360e00190f35b3480156106cd57600080fd5b50610223600480360360608110156106e457600080fd5b810190602081018135600160201b8111156106fe57600080fd5b82018360208201111561071057600080fd5b803590602001918460208302840111600160201b8311171561073157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561078057600080fd5b82018360208201111561079257600080fd5b803590602001918460208302840111600160201b831117156107b357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561080257600080fd5b82018360208201111561081457600080fd5b803590602001918460208302840111600160201b8311171561083557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612152945050505050565b34801561087f57600080fd5b5061025d6121ea565b34801561089457600080fd5b5061025d6121f0565b3480156108a957600080fd5b50610223600480360360808110156108c057600080fd5b50803590602081013590604081013590606001351515612200565b3480156108e757600080fd5b5061025d600480360360208110156108fe57600080fd5b50356001600160a01b0316612358565b34801561091a57600080fd5b506102236004803603602081101561093157600080fd5b5035612502565b34801561094457600080fd5b50610223600480360360a081101561095b57600080fd5b50803590602081013590604081013590606081013590608001351515612774565b34801561098857600080fd5b5061025d6004803603602081101561099f57600080fd5b50356001600160a01b03166128dc565b61022360048036036101608110156109c657600080fd5b810190602081018135600160201b8111156109e057600080fd5b8201836020820111156109f257600080fd5b803590602001918460208302840111600160201b83111715610a1357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505082359350506001600160a01b036020830135811692604081013592506060810135916080820135169060a081013515159060c08101359060e081013515159061010081013590610120013515156128ee565b610223600480360360e0811015610aaf57600080fd5b506001600160a01b03813581169160208101359160408201351690606081013590608081013515159060a08101359060c001351515612cf0565b348015610af557600080fd5b5061055b60048036036040811015610b0c57600080fd5b810190602081018135600160201b811115610b2657600080fd5b820183602082011115610b3857600080fd5b803590602001918460208302840111600160201b83111715610b5957600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250612da1915050565b348015610ba557600080fd5b5061022360048036036080811015610bbc57600080fd5b50803590602081013590604081013590606001351515612fb8565b348015610be357600080fd5b5061022360048036036020811015610bfa57600080fd5b50356001600160a01b0316613181565b348015610c1657600080fd5b50610c4360048036036040811015610c2d57600080fd5b506001600160a01b03813516906020013561322b565b604080516001600160a01b039a8b168152988a1660208a015296909816878701526060870194909452608086019290925260a0850152151560c0840152151560e08301526101008201929092529051908190036101200190f35b348015610ca957600080fd5b5061022360048036036060811015610cc057600080fd5b506001600160a01b038135811691602081013591604090910135166133e8565b348015610cec57600080fd5b50610d1960048036036040811015610d0357600080fd5b506001600160a01b038135169060200135613851565b604080516001600160a01b039a8b1681526020810199909952968916888801529490971660608701526080860192909252151560a085015260c084015292151560e08301526101008201929092529051908190036101200190f35b348015610d8057600080fd5b5061025d60048036036020811015610d9757600080fd5b50356001600160a01b0316613937565b348015610db357600080fd5b50610223600480360360c0811015610dca57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060808101359060a00135613949565b348015610e0c57600080fd5b50610e3960048036036040811015610e2357600080fd5b506001600160a01b038135169060200135613ab8565b604080516001600160a01b039a8b168152988a1660208a0152888101979097529490971660608701526080860192909252151560a085015260c084015292151560e08301526101008201929092529051908190036101200190f35b348015610ea057600080fd5b506103bf613b22565b348015610eb557600080fd5b5061022360048036036020811015610ecc57600080fd5b5035613b31565b348015610edf57600080fd5b506103bf613e6e565b348015610ef457600080fd5b506103bf613e7d565b348015610f0957600080fd5b5061022360048036036020811015610f2057600080fd5b5035613e8c565b60066020526000908152604090205481565b600080600080600080600080610f4d614fd0565b505050506001600160a01b03968716600090815260036020818152604080842099845298815291889020885161012081018a5281548b16815260018201548b1693810184905260028201549981018a9052918101549099166060820181905260048a01546080830181905260058b015460ff908116151560a0850181905260068d015460c0860181905260078e0154909216151560e086018190526008909d0154610100909501859052949c9a9b929a91995093975092955093509150565b60026000541415611052576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b600260005561105f61501c565b6001600160a01b038085166000908152600560209081526040808320878452825291829020825161010081018452815490941684526001810180548451818502810185019095528085529193858401939092908301828280156110eb57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116110cd575b5050509183525050600282015460208201526003820154604082015260048201546060820152600582015460ff8082161515608084015261010090910416151560a082015260069091015460c09091015280519091506001600160a01b0316611189576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b8060a00151156111e0576111a581602001518260800151612da1565b6111e05760405162461bcd60e51b81526004018080602001828103825260268152602001806152f16026913960400191505060405180910390fd5b6001600160a01b0384166000908152600560209081526040808320868452909152812080546001600160a01b03191681559061121f600183018261506e565b50600060028201819055600382018190556004820181905560058201805461ffff191690556006909101819055600b54604083015160208401518051611292946001600160a01b03909416939061127257fe5b60200260200101516001600160a01b0316613f1d9092919063ffffffff16565b600854602082015180516000926001600160a01b0316919060001981019081106112b857fe5b60200260200101516001600160a01b03161480156112d757508160c001515b15611304576112ef8260200151836060015130613f74565b90506112ff8183600001516140a3565b61131e565b61131b826020015183606001518460000151613f74565b90505b61132c8260e00151846140a3565b846001600160a01b03167f7e1fe496989eea92b738a562dbf9c0ae6aa6fcf3f1ef09e95ee4f7603721706b858460200151856040015186606001518688608001518960a001518a60c001518b60e00151604051808a8152602001806020018981526020018881526020018781526020018681526020018515158152602001841515815260200183815260200182810382528a818151815260200191508051906020019060200280838360005b838110156113f05781810151838201526020016113d8565b505050509050019a505050505050505050505060405180910390a250506001600055505050565b6007546001600160a01b0316331461146d576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600d8190556040805182815290517fe46d9daf6d25f7615efa1d0183b90ac6759d85014b598e409aadf0fd918d59a69181900360200190a150565b600260005414156114ee576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000556114fb614fd0565b506001600160a01b03808416600090815260036020818152604080842087855282529283902083516101208101855281548616808252600183015487169382019390935260028201549481019490945291820154909316606083015260048101546080830152600581015460ff908116151560a0840152600682015460c0840152600782015416151560e083015260080154610100820152906115d3576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b60006115f48260e001518360c0015184606001518560a00151156001611f8f565b506001600160a01b0380871660009081526003602081815260408084208a8552825280842080546001600160a01b0319908116825560018201805482169055600282018690559381018054909416909355600480840185905560058401805460ff199081169091556006850186905560078501805490911690556008909301849055600a5488518984015160608b01518b85015160808d015160a08e01518751632662166b60e01b8152958c1699860199909952928a16602485015290891660448401526064830152608482015293151560a48501523060c4850152905195965092949290931692632662166b9260e48084019382900301818787803b1580156116fd57600080fd5b505af1158015611711573d6000803e3d6000fd5b505050506040513d602081101561172757600080fd5b505160085460208501519192506001600160a01b039182169116141561175a576117558184600001516140a3565b611777565b82516020840151611777916001600160a01b039091169083613f1d565b611786836101000151856140a3565b82600001516001600160a01b03167f9a382661d6573da86db000471303be6f0b2b1bb66089b08e3c16a85d7b6e94f88685602001518660400151876060015188608001518960a001518a60c001518b60e001518c61010001518c604051808b81526020018a6001600160a01b03168152602001898152602001886001600160a01b03168152602001878152602001861515815260200185815260200184151581526020018381526020018281526020019a505050505050505050505060405180910390a25050600160005550505050565b6007546001600160a01b031681565b600260005414156118ac576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000819055885114806118c2575087516003145b611901576040805162461bcd60e51b815260206004820152601f6024820152600080516020615317833981519152604482015290519081900360640190fd5b8760018951038151811061191157fe5b60200260200101516001600160a01b03168860008151811061192f57fe5b60200260200101516001600160a01b0316141561198e576040805162461bcd60e51b815260206004820152601860248201527709ee4c8cae484deded67440d2dcecc2d8d2c840bee0c2e8d60431b604482015290519081900360640190fd5b600087116119e2576040805162461bcd60e51b815260206004820152601c60248201527b27b93232b92137b7b59d1034b73b30b634b2102fb0b6b7bab73a24b760211b604482015290519081900360640190fd5b600c54831015611a235760405162461bcd60e51b81526004018080602001828103825260258152602001806152856025913960400191505060405180910390fd5b611a2b614120565b8115611ae65760085488516001600160a01b03909116908990600090611a4d57fe5b60200260200101516001600160a01b031614611a9a5760405162461bcd60e51b81526004018080602001828103825260258152602001806152606025913960400191505060405180910390fd5b611aa48388614192565b3414611ae15760405162461bcd60e51b81526004018080602001828103825260268152602001806152cb6026913960400191505060405180910390fd5b611bd0565b823414611b245760405162461bcd60e51b815260040180806020018281038252602e815260200180615361602e913960400191505060405180910390fd5b600a5488516001600160a01b0390911690631b827878908a90600090611b4657fe5b602002602001015133308b6040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b03168152602001828152602001945050505050600060405180830381600087803b158015611bb757600080fd5b505af1158015611bcb573d6000803e3d6000fd5b505050505b611be0338989898989878a6141ea565b50506001600055505050505050565b6001602081815260009384526040808520909152918352912080549181015460028201546003830154600484015460058501546006860154600787015460088801546009909801546001600160a01b03998a1699978816989697958616969490951694929360ff92831693919216908a565b600e5460ff1681565b6008546001600160a01b031681565b60026000541415611cbf576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055611ccc61508f565b5033600090815260016020818152604080842085855282529283902083516101408101855281546001600160a01b039081168083529483015481169382019390935260028201549481019490945260038101548216606085015260048101549091166080840152600581015460a0840152600681015460ff908116151560c0850152600782015460e0850152600882015416151561010084015260090154610120830152611daf576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b3360009081526001602081815260408084208685528252832080546001600160a01b0319908116825592810180548416905560028101849055600381018054841690556004810180549093169092556005820183905560068201805460ff19908116909155600783018490556008808401805490921690915560099092019290925554908201516001600160a01b0390811691161415611e7357611e6e611e68826040015183610120015161419290919063ffffffff16565b336140a3565b611ea8565b611e9933826040015183602001516001600160a01b0316613f1d9092919063ffffffff16565b611ea8816101200151336140a3565b80600001516001600160a01b03167fd500f34e0ec655b7614ae42e1d9c666d5e4dde909a1297829f8c5ecf00805d328383602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b6101200151604051808b81526020018a6001600160a01b03168152602001898152602001886001600160a01b03168152602001876001600160a01b03168152602001868152602001851515815260200184815260200183151581526020018281526020019a505050505050505050505060405180910390a250506001600055565b670de0b6b3a764000081565b60008060008461201757600b54604080516340d3096b60e11b81526001600160a01b038981166004830152915191909216916381a612d6916024808301926020929190829003018186803b158015611fe657600080fd5b505afa158015611ffa573d6000803e3d6000fd5b505050506040513d602081101561201057600080fd5b5051612091565b600b5460408051637092736960e11b81526001600160a01b0389811660048301529151919092169163e124e6d2916024808301926020929190829003018186803b15801561206457600080fd5b505afa158015612078573d6000803e3d6000fd5b505050506040513d602081101561208e57600080fd5b50515b90506000886120a2578782106120a6565b8782115b905084156120ea57806120ea5760405162461bcd60e51b81526004018080602001828103825260268152602001806152f16026913960400191505060405180910390fd5b909890975095505050505050565b600c5481565b600560208181526000938452604080852090915291835291208054600282015460038301546004840154948401546006909401546001600160a01b03909316949193909260ff808316926101009004169087565b60005b83518110156121825761217a84828151811061216d57fe5b6020026020010151613b31565b600101612155565b5060005b82518110156121b3576121ab83828151811061219e57fe5b6020026020010151611c79565b600101612186565b5060005b81518110156121e4576121dc8282815181106121cf57fe5b6020026020010151612502565b6001016121b7565b50505050565b600d5481565b68327cb2734119d3b7a9601e1b81565b60026000541415612246576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000908155338152600160209081526040808320878452909152902080546001600160a01b03166122ae576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b6007810183905560088101805483151560ff19909116811790915560058201859055600382015460048301546006840154604080518a81526001600160a01b039485166020820152929093168284015260ff16151560608201526080810187905260a0810186905260c08101929092525133917f0a0360dd5c354235bbf8d386ba3b24ef8134088e0785677de1504df219d9149a919081900360e00190a250506001600055505050565b600b5460408051632c668ec160e01b81526001600160a01b038481166004830152670de0b6b3a76400006024830152915160009384931691632c668ec1916044808301926020929190829003018186803b1580156123b557600080fd5b505afa1580156123c9573d6000803e3d6000fd5b505050506040513d60208110156123df57600080fd5b5051600b54604080516340d3096b60e11b81526001600160a01b038781166004830152915193945060009391909216916381a612d6916024808301926020929190829003018186803b15801561243457600080fd5b505afa158015612448573d6000803e3d6000fd5b505050506040513d602081101561245e57600080fd5b5051600b54604080516323b95ceb60e21b81526001600160a01b03888116600483015291519394506000939190921691638ee573ac916024808301926020929190829003018186803b1580156124b357600080fd5b505afa1580156124c7573d6000803e3d6000fd5b505050506040513d60208110156124dd57600080fd5b505190506124f9600a82900a6124f385856143f9565b90614452565b95945050505050565b60026000541415612548576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055612555614fd0565b5033600090815260036020818152604080842085855282529283902083516101208101855281546001600160a01b03908116808352600184015482169483019490945260028301549582019590955292810154909316606083015260048301546080830152600583015460ff908116151560a0840152600684015460c0840152600784015416151560e08301526008909201546101008201529061262e576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b336000818152600360208181526040808420878552909152822080546001600160a01b03199081168255600182018054821690556002820184905591810180549092169091556004810182905560058101805460ff19908116909155600682018390556007820180549091169055600801556101008201516126af916140a3565b80600001516001600160a01b03167f1154174c82984656b028c8021671988f60a346497e56fe02554761184f82a0758383602001518460400151856060015186608001518760a001518860c001518960e001518a6101000151604051808a8152602001896001600160a01b03168152602001888152602001876001600160a01b0316815260200186815260200185151581526020018481526020018315158152602001828152602001995050505050505050505060405180910390a250506001600055565b600260005414156127ba576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000908155338152600360209081526040808320888452909152902080546001600160a01b0316612822576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b6006810183905560078101805483151560ff1990911681179091556004820185905560028201869055600182015460038301546005840154604080518b81526001600160a01b0394851660208201528082018b90529290931660608301526080820188905260ff16151560a082015260c0810186905260e08101929092525133917f75781255bc71c83f89f29e5a2599f2c174a562d2cd8f2e818a47f132e728049891908190036101000190a25050600160005550505050565b60026020526000908152604090205481565b60026000541415612934576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055612941614120565b600c548210156129825760405162461bcd60e51b81526004018080602001828103825260258152602001806152856025913960400191505060405180910390fd5b8015612a3d576008548b516001600160a01b03909116908c906000906129a457fe5b60200260200101516001600160a01b0316146129f15760405162461bcd60e51b81526004018080602001828103825260258152602001806152606025913960400191505060405180910390fd5b6129fb828b614192565b3414612a385760405162461bcd60e51b81526004018080602001828103825260268152602001806152cb6026913960400191505060405180910390fd5b612b27565b813414612a7b5760405162461bcd60e51b815260040180806020018281038252602e815260200180615361602e913960400191505060405180910390fd5b600a548b516001600160a01b0390911690631b827878908d90600090612a9d57fe5b602002602001015133308e6040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b03168152602001828152602001945050505050600060405180830381600087803b158015612b0e57600080fd5b505af1158015612b22573d6000803e3d6000fd5b505050505b60008b60018d510381518110612b3957fe5b60200260200101519050600060018d511115612bfe57816001600160a01b03168d600081518110612b6657fe5b60200260200101516001600160a01b03161415612bc5576040805162461bcd60e51b815260206004820152601860248201527709ee4c8cae484deded67440d2dcecc2d8d2c840bee0c2e8d60431b604482015290519081900360640190fd5b612bec600b60009054906101000a90046001600160a01b03168d8f60008151811061127257fe5b612bf78d8b30613f74565b9050612c01565b508a5b600b5460408051630a48d5a960e01b81526001600160a01b0385811660048301526024820185905291516000939290921691630a48d5a991604480820192602092909190829003018186803b158015612c5957600080fd5b505afa158015612c6d573d6000803e3d6000fd5b505050506040513d6020811015612c8357600080fd5b5051600d54909150811015612cc95760405162461bcd60e51b81526004018080602001828103825260228152602001806151bd6022913960400191505060405180910390fd5b50612cdc3383838b8f8e8d8d8d8d614491565b505060016000555050505050505050505050565b60026000541415612d36576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055612d43614120565b600c543411612d835760405162461bcd60e51b81526004018080602001828103825260258152602001806152856025913960400191505060405180910390fd5b612d933386868a8a888888614764565b505060016000555050505050565b6000825160021480612db4575082516003145b612df3576040805162461bcd60e51b815260206004820152601f6024820152600080516020615317833981519152604482015290519081900360640190fd5b600083600081518110612e0257fe5b60200260200101519050600084600186510381518110612e1e57fe5b602090810291909101015160095490915060009081906001600160a01b0385811691161415612e6b57612e6487600181518110612e5757fe5b6020026020010151612358565b9150612ee7565b600b54604080516340d3096b60e11b81526001600160a01b038781166004830152915191909216916381a612d6916024808301926020929190829003018186803b158015612eb857600080fd5b505afa158015612ecc573d6000803e3d6000fd5b505050506040513d6020811015612ee257600080fd5b505191505b6009546001600160a01b0384811691161415612f10575068327cb2734119d3b7a9601e1b612f8c565b600b5460408051637092736960e11b81526001600160a01b0386811660048301529151919092169163e124e6d2916024808301926020929190829003018186803b158015612f5d57600080fd5b505afa158015612f71573d6000803e3d6000fd5b505050506040513d6020811015612f8757600080fd5b505190505b6000612fa8836124f38468327cb2734119d3b7a9601e1b6143f9565b8710955050505050505b92915050565b60026000541415612ffe576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b60026000908155338152600560209081526040808320878452909152902080546001600160a01b0316613066576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b838160030181905550828160040181905550818160050160006101000a81548160ff021916908315150217905550336001600160a01b03167fa7f9f4a25eb76f5ec01b1a429d95d6a00833f0f137c88827c58799a1c1ff0dfe868360010184600201548888888860050160019054906101000a900460ff168960060154604051808981526020018060200188815260200187815260200186815260200185151581526020018415158152602001838152602001828103825289818154815260200191508054801561316057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613142575b5050995050505050505050505060405180910390a250506001600055505050565b6007546001600160a01b031633146131d7576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600780546001600160a01b0383166001600160a01b0319909116811790915560408051918252517fe24c39186e9137521953beaa8446e71f55b8f12296984f9d4273ceb1af728d909181900360200190a150565b600080600080600080600080600061324161501c565b6001600160a01b03808d1660009081526005602090815260408083208f8452825291829020825161010081018452815490941684526001810180548451818502810185019095528085529193858401939092908301828280156132cd57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116132af575b505050918352505060028201546020808301919091526003830154604083015260048301546060830152600583015460ff8082161515608085015261010090910416151560a083015260069092015460c0909101528101515190915061333457600061334e565b806020015160008151811061334557fe5b60200260200101515b60018260200151511161336257600061337c565b816020015160018151811061337357fe5b60200260200101515b6002836020015151116133905760006133aa565b82602001516002815181106133a157fe5b60200260200101515b8360400151846060015185608001518660a001518760c001518860e00151995099509950995099509950995099509950509295985092959850929598565b6002600054141561342e576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b600260005561343b61508f565b506001600160a01b038084166000908152600160208181526040808420878552825292839020835161014081018552815486168082529382015486169281019290925260028101549382019390935260038301548416606082015260048301549093166080840152600582015460a0840152600682015460ff908116151560c0850152600783015460e08501526008830154161515610100840152600990910154610120830152613521576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b60006135428261010001518360e0015184608001518560c001516001611f8f565b506001600160a01b0380871660009081526001602081815260408084208a8552825280842080546001600160a01b0319908116825593810180548516905560028101859055600381018054851690556004810180549094169093556005830184905560068301805460ff19908116909155600784018590556008840180549091169055600990920192909255600b5490860151918601519394506135ea938316921690613f1d565b81606001516001600160a01b031682602001516001600160a01b0316146136c257604080516002808252606080830184529260208301908036833701905050905082602001518160008151811061363d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505082606001518160018151811061366f57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600061369d82600030613f74565b600b5460608601519192506136bf916001600160a01b03908116911683613f1d565b50505b600a5482516060840151608085015160a086015160c087015160408051630f8ee8bb60e11b81526001600160a01b03968716600482015294861660248601529285166044850152606484019190915215156084830152519190921691631f1dd1769160a480830192600092919082900301818387803b15801561374457600080fd5b505af1158015613758573d6000803e3d6000fd5b5050505061376b826101200151846140a3565b81600001516001600160a01b03167f7fb1c74d1ea6aa1c9c585e17ce8274c8ff98745e85e7459b73f87d784494f58e8584602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001518c61012001518c604051808c81526020018b6001600160a01b031681526020018a8152602001896001600160a01b03168152602001886001600160a01b03168152602001878152602001861515815260200185815260200184151581526020018381526020018281526020019b50505050505050505050505060405180910390a250506001600055505050565b600080600080600080600080600061386761508f565b505050506001600160a01b0397881660009081526001602081815260408084209a845299815291899020895161014081018b5281548c168152918101548b1692820183905260028101549982018a905260038101548b16606083018190526004820154909b1660808301819052600582015460a08401819052600683015460ff908116151560c08601819052600785015460e08701819052600886015490921615156101008701819052600990950154610120909601869052959e9c9d9c929b5090995093975092955093509150565b60046020526000908152604090205481565b6007546001600160a01b0316331461399f576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600e5460ff16156139f7576040805162461bcd60e51b815260206004820152601e60248201527f4f72646572426f6f6b3a20616c726561647920696e697469616c697a65640000604482015290519081900360640190fd5b600e805460ff19166001179055600a80546001600160a01b038089166001600160a01b03199283168117909355600b8054898316908416811790915560088054898416908516811790915560098054938916939094168317909355600c869055600d8590556040805194855260208501919091528381019290925260608301526080820184905260a08201839052517fcfb7ef8749fafc8da2af1ba3d025479ffc4e58f7dc420113e112512a3bda59639181900360c00190a1505050505050565b600360208181526000938452604080852090915291835291208054600182015460028301549383015460048401546005850154600686015460078701546008909701546001600160a01b039687169895871697959690941694929360ff9283169391929091169089565b6009546001600160a01b031681565b60026000541415613b77576040805162461bcd60e51b815260206004820152601f602482015260008051602061519d833981519152604482015290519081900360640190fd5b6002600055613b8461501c565b33600090815260056020908152604080832085845282529182902082516101008101845281546001600160a01b0316815260018201805485518186028101860190965280865291949293858101939290830182828015613c0d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613bef575b5050509183525050600282015460208201526003820154604082015260048201546060820152600582015460ff8082161515608084015261010090910416151560a082015260069091015460c09091015280519091506001600160a01b0316613cab576040805162461bcd60e51b815260206004820152601d602482015260008051602061517d833981519152604482015290519081900360640190fd5b336000908152600560209081526040808320858452909152812080546001600160a01b031916815590613ce1600183018261506e565b50600060028201819055600382018190556004820181905560058201805461ffff191690556006909101819055600854602083015180516001600160a01b03909216929091613d2c57fe5b60200260200101516001600160a01b03161415613d6657613d61611e6882604001518360e0015161419290919063ffffffff16565b613d8e565b613d80338260400151836020015160008151811061127257fe5b613d8e8160e00151336140a3565b336001600160a01b03167fefd66d4f9c2f880c70aedeb5b26a44fb474cea07e5d6c533f2d27c303d5d94538383602001518460400151856060015186608001518760a001518860c001518960e00151604051808981526020018060200188815260200187815260200186815260200185151581526020018415158152602001838152602001828103825289818151815260200191508051906020019060200280838360005b83811015613e4b578181015183820152602001613e33565b50505050905001995050505050505050505060405180910390a250506001600055565b600a546001600160a01b031681565b600b546001600160a01b031681565b6007546001600160a01b03163314613ee2576040805162461bcd60e51b815260206004820152601460248201527327b93232b92137b7b59d103337b93134b23232b760611b604482015290519081900360640190fd5b600c8190556040805182815290517fbde5eafdc37b81830d70124cddccaaa6d034e71dda3c8fc18a959ca76a7cbcfc9181900360200190a150565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613f6f9084906149da565b505050565b6000835160021415613fbb57613fb484600081518110613f9057fe5b602002602001015185600181518110613fa557fe5b60200260200101518585614a8b565b905061409c565b835160031415614061576000613ffc85600081518110613fd757fe5b602002602001015186600181518110613fec57fe5b6020026020010151600030614a8b565b9050614025600b60009054906101000a90046001600160a01b0316828760018151811061127257fe5b6140598560018151811061403557fe5b60200260200101518660028151811061404a57fe5b60200260200101518686614a8b565b91505061409c565b6040805162461bcd60e51b815260206004820152601f6024820152600080516020615317833981519152604482015290519081900360640190fd5b9392505050565b60085460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921691632e1a7d4d9160248082019260009290919082900301818387803b1580156140f057600080fd5b505af1158015614104573d6000803e3d6000fd5b5061411c925050506001600160a01b03821683614c6a565b5050565b341561419057600860009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561417657600080fd5b505af115801561418a573d6000803e3d6000fd5b50505050505b565b60008282018381101561409c576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6001600160a01b03881660009081526006602052604090205461420b61501c565b6040518061010001604052808b6001600160a01b031681526020018a81526020018981526020018881526020018781526020018615158152602001851515815260200184815250905061426860018361419290919063ffffffff16565b6001600160a01b038b8116600090815260066020908152604080832094909455600581528382208683528152929020835181546001600160a01b031916921691909117815582820151805184936142c69260018501929101906150e3565b5060408201518160020155606082015181600301556080820151816004015560a08201518160050160006101000a81548160ff02191690831515021790555060c08201518160050160016101000a81548160ff02191690831515021790555060e08201518160060155905050896001600160a01b03167fdf06bb56ffc4029dc0b62b68bb5bbadea93a38b530cefc9b81afb742a6555d88838b8b8b8b8b8b8b604051808981526020018060200188815260200187815260200186815260200185151581526020018415158152602001838152602001828103825289818151815260200191508051906020019060200280838360005b838110156143d35781810151838201526020016143bb565b50505050905001995050505050505050505060405180910390a250505050505050505050565b60008261440857506000612fb2565b8282028284828161441557fe5b041461409c5760405162461bcd60e51b81526004018080602001828103825260218152602001806152aa6021913960400191505060405180910390fd5b600061409c83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250614d4f565b336000908152600260205260409020546144a961508f565b6040518061014001604052808d6001600160a01b031681526020018c6001600160a01b031681526020018b81526020018a6001600160a01b03168152602001896001600160a01b031681526020018881526020018715158152602001868152602001851515815260200184815250905061452d60018361419290919063ffffffff16565b600260008e6001600160a01b03166001600160a01b031681526020019081526020016000208190555080600160008e6001600160a01b03166001600160a01b03168152602001908152602001600020600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816002015560608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a0820151816005015560c08201518160060160006101000a81548160ff02191690831515021790555060e082015181600701556101008201518160080160006101000a81548160ff02191690831515021790555061012082015181600901559050508b6001600160a01b03167fb27b9afe3043b93788c40cfc3cc73f5d928a2e40f3ba01820b246426de8fa1b9838d8d8d8d8d8d8d8d8d604051808b81526020018a6001600160a01b03168152602001898152602001886001600160a01b03168152602001876001600160a01b03168152602001868152602001851515815260200184815260200183151581526020018281526020019a505050505050505050505060405180910390a2505050505050505050505050565b6001600160a01b038816600090815260046020526040902054614785614fd0565b5060408051610120810182526001600160a01b03808c1682528a8116602083015291810189905290871660608201526080810186905284151560a082015260c0810184905282151560e0820152346101008201526147e4826001614192565b600460008c6001600160a01b03166001600160a01b031681526020019081526020016000208190555080600360008c6001600160a01b03166001600160a01b03168152602001908152602001600020600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816002015560608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506080820151816004015560a08201518160050160006101000a81548160ff02191690831515021790555060c0820151816006015560e08201518160070160006101000a81548160ff0219169083151502179055506101008201518160080155905050896001600160a01b03167f48ee333d2a65cc45fdb83bc012920d89181c3377390cd239d2b63f2bef67a02d838b8b8b8b8b8b8b34604051808a8152602001896001600160a01b03168152602001888152602001876001600160a01b0316815260200186815260200185151581526020018481526020018315158152602001828152602001995050505050505050505060405180910390a250505050505050505050565b6060614a2f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614df19092919063ffffffff16565b805190915015613f6f57808060200190516020811015614a4e57600080fd5b5051613f6f5760405162461bcd60e51b815260040180806020018281038252602a815260200180615337602a913960400191505060405180910390fd5b60095460009081906001600160a01b0386811691161415614b3057600b546040805163817bb85760e01b81526001600160a01b03898116600483015286811660248301529151919092169163817bb8579160448083019260209291908290030181600087803b158015614afd57600080fd5b505af1158015614b11573d6000803e3d6000fd5b505050506040513d6020811015614b2757600080fd5b50519050614c2b565b6009546001600160a01b0387811691161415614b9d57600b5460408051630711e61960e41b81526001600160a01b03888116600483015286811660248301529151919092169163711e61909160448083019260209291908290030181600087803b158015614afd57600080fd5b600b5460408051634998b10960e11b81526001600160a01b038981166004830152888116602483015286811660448301529151919092169163933162129160648083019260209291908290030181600087803b158015614bfc57600080fd5b505af1158015614c10573d6000803e3d6000fd5b505050506040513d6020811015614c2657600080fd5b505190505b838110156124f95760405162461bcd60e51b815260040180806020018281038252602181526020018061523f6021913960400191505060405180910390fd5b80471015614cbf576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015290519081900360640190fd5b6040516000906001600160a01b0384169083908381818185875af1925050503d8060008114614d0a576040519150601f19603f3d011682016040523d82523d6000602084013e614d0f565b606091505b5050905080613f6f5760405162461bcd60e51b815260040180806020018281038252603a8152602001806151df603a913960400191505060405180910390fd5b60008183614ddb5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614da0578181015183820152602001614d88565b50505050905090810190601f168015614dcd5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581614de757fe5b0495945050505050565b6060614e008484600085614e08565b949350505050565b606082471015614e495760405162461bcd60e51b81526004018080602001828103825260268152602001806152196026913960400191505060405180910390fd5b614e5285614f64565b614ea3576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310614ee25780518252601f199092019160209182019101614ec3565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614f44576040519150601f19603f3d011682016040523d82523d6000602084013e614f49565b606091505b5091509150614f59828286614f6a565b979650505050505050565b3b151590565b60608315614f7957508161409c565b825115614f895782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315614da0578181015183820152602001614d88565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b60405180610100016040528060006001600160a01b0316815260200160608152602001600081526020016000815260200160008152602001600015158152602001600015158152602001600081525090565b508054600082559060005260206000209081019061508c9190615148565b50565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081019190915290565b828054828255906000526020600020908101928215615138579160200282015b8281111561513857825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615103565b5061514492915061515d565b5090565b5b808211156151445760008155600101615149565b5b808211156151445780546001600160a01b031916815560010161515e56fe4f72646572426f6f6b3a206e6f6e2d6578697374656e74206f726465720000005265656e7472616e637947756172643a207265656e7472616e742063616c6c004f72646572426f6f6b3a20696e73756666696369656e7420636f6c6c61746572616c416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d61792068617665207265766572746564416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c4f72646572426f6f6b3a20696e73756666696369656e7420616d6f756e744f75744f72646572426f6f6b3a206f6e6c79207765746820636f756c6420626520777261707065644f72646572426f6f6b3a20696e73756666696369656e7420657865637574696f6e20666565536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f72646572426f6f6b3a20696e636f72726563742076616c7565207472616e736665727265644f72646572426f6f6b3a20696e76616c696420707269636520666f7220657865637574696f6e4f72646572426f6f6b3a20696e76616c6964205f706174682e6c656e677468005361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565644f72646572426f6f6b3a20696e636f727265637420657865637574696f6e20666565207472616e73666572726564a2646970667358221220e85a1f264782d8faab5f0f3dab3cf5b8844d298c594ad34ab45699fcaa9855e464736f6c634300060c0033"; + public OrderBookDeploymentBase() : base(BYTECODE) { } + public OrderBookDeploymentBase(string byteCode) : base(byteCode) { } + + } + + public partial class PricePrecisionFunction : PricePrecisionFunctionBase { } + + [Function("PRICE_PRECISION", "uint256")] + public class PricePrecisionFunctionBase : FunctionMessage + { + + } + + public partial class UsdgPrecisionFunction : UsdgPrecisionFunctionBase { } + + [Function("USDG_PRECISION", "uint256")] + public class UsdgPrecisionFunctionBase : FunctionMessage + { + + } + + public partial class CancelDecreaseOrderFunction : CancelDecreaseOrderFunctionBase { } + + [Function("cancelDecreaseOrder")] + public class CancelDecreaseOrderFunctionBase : FunctionMessage + { + [Parameter("uint256", "_orderIndex", 1)] + public virtual BigInteger OrderIndex { get; set; } + } + + public partial class CancelIncreaseOrderFunction : CancelIncreaseOrderFunctionBase { } + + [Function("cancelIncreaseOrder")] + public class CancelIncreaseOrderFunctionBase : FunctionMessage + { + [Parameter("uint256", "_orderIndex", 1)] + public virtual BigInteger OrderIndex { get; set; } + } + + public partial class CancelMultipleFunction : CancelMultipleFunctionBase { } + + [Function("cancelMultiple")] + public class CancelMultipleFunctionBase : FunctionMessage + { + [Parameter("uint256[]", "_swapOrderIndexes", 1)] + public virtual List SwapOrderIndexes { get; set; } + [Parameter("uint256[]", "_increaseOrderIndexes", 2)] + public virtual List IncreaseOrderIndexes { get; set; } + [Parameter("uint256[]", "_decreaseOrderIndexes", 3)] + public virtual List DecreaseOrderIndexes { get; set; } + } + + public partial class CancelSwapOrderFunction : CancelSwapOrderFunctionBase { } + + [Function("cancelSwapOrder")] + public class CancelSwapOrderFunctionBase : FunctionMessage + { + [Parameter("uint256", "_orderIndex", 1)] + public virtual BigInteger OrderIndex { get; set; } + } + + public partial class CreateDecreaseOrderFunction : CreateDecreaseOrderFunctionBase { } + + [Function("createDecreaseOrder")] + public class CreateDecreaseOrderFunctionBase : FunctionMessage + { + [Parameter("address", "_indexToken", 1)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_sizeDelta", 2)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("address", "_collateralToken", 3)] + public virtual string CollateralToken { get; set; } + [Parameter("uint256", "_collateralDelta", 4)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "_triggerPrice", 6)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "_triggerAboveThreshold", 7)] + public virtual bool TriggerAboveThreshold { get; set; } + } + + public partial class CreateIncreaseOrderFunction : CreateIncreaseOrderFunctionBase { } + + [Function("createIncreaseOrder")] + public class CreateIncreaseOrderFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("uint256", "_amountIn", 2)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("address", "_indexToken", 3)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_minOut", 4)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_sizeDelta", 5)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("address", "_collateralToken", 6)] + public virtual string CollateralToken { get; set; } + [Parameter("bool", "_isLong", 7)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "_triggerPrice", 8)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "_triggerAboveThreshold", 9)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "_executionFee", 10)] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("bool", "_shouldWrap", 11)] + public virtual bool ShouldWrap { get; set; } + } + + public partial class CreateSwapOrderFunction : CreateSwapOrderFunctionBase { } + + [Function("createSwapOrder")] + public class CreateSwapOrderFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("uint256", "_amountIn", 2)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "_minOut", 3)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_triggerRatio", 4)] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "_triggerAboveThreshold", 5)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "_executionFee", 6)] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("bool", "_shouldWrap", 7)] + public virtual bool ShouldWrap { get; set; } + [Parameter("bool", "_shouldUnwrap", 8)] + public virtual bool ShouldUnwrap { get; set; } + } + + public partial class DecreaseOrdersFunction : DecreaseOrdersFunctionBase { } + + [Function("decreaseOrders", typeof(DecreaseOrdersOutputDTO))] + public class DecreaseOrdersFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + [Parameter("uint256", "", 2)] + public virtual BigInteger ReturnValue2 { get; set; } + } + + public partial class DecreaseOrdersIndexFunction : DecreaseOrdersIndexFunctionBase { } + + [Function("decreaseOrdersIndex", "uint256")] + public class DecreaseOrdersIndexFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class ExecuteDecreaseOrderFunction : ExecuteDecreaseOrderFunctionBase { } + + [Function("executeDecreaseOrder")] + public class ExecuteDecreaseOrderFunctionBase : FunctionMessage + { + [Parameter("address", "_address", 1)] + public virtual string Address { get; set; } + [Parameter("uint256", "_orderIndex", 2)] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "_feeReceiver", 3)] + public virtual string FeeReceiver { get; set; } + } + + public partial class ExecuteIncreaseOrderFunction : ExecuteIncreaseOrderFunctionBase { } + + [Function("executeIncreaseOrder")] + public class ExecuteIncreaseOrderFunctionBase : FunctionMessage + { + [Parameter("address", "_address", 1)] + public virtual string Address { get; set; } + [Parameter("uint256", "_orderIndex", 2)] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "_feeReceiver", 3)] + public virtual string FeeReceiver { get; set; } + } + + public partial class ExecuteSwapOrderFunction : ExecuteSwapOrderFunctionBase { } + + [Function("executeSwapOrder")] + public class ExecuteSwapOrderFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("uint256", "_orderIndex", 2)] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "_feeReceiver", 3)] + public virtual string FeeReceiver { get; set; } + } + + public partial class GetDecreaseOrderFunction : GetDecreaseOrderFunctionBase { } + + [Function("getDecreaseOrder", typeof(GetDecreaseOrderOutputDTO))] + public class GetDecreaseOrderFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("uint256", "_orderIndex", 2)] + public virtual BigInteger OrderIndex { get; set; } + } + + public partial class GetIncreaseOrderFunction : GetIncreaseOrderFunctionBase { } + + [Function("getIncreaseOrder", typeof(GetIncreaseOrderOutputDTO))] + public class GetIncreaseOrderFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("uint256", "_orderIndex", 2)] + public virtual BigInteger OrderIndex { get; set; } + } + + public partial class GetSwapOrderFunction : GetSwapOrderFunctionBase { } + + [Function("getSwapOrder", typeof(GetSwapOrderOutputDTO))] + public class GetSwapOrderFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("uint256", "_orderIndex", 2)] + public virtual BigInteger OrderIndex { get; set; } + } + + public partial class GetUsdgMinPriceFunction : GetUsdgMinPriceFunctionBase { } + + [Function("getUsdgMinPrice", "uint256")] + public class GetUsdgMinPriceFunctionBase : FunctionMessage + { + [Parameter("address", "_otherToken", 1)] + public virtual string OtherToken { get; set; } + } + + public partial class GovFunction : GovFunctionBase { } + + [Function("gov", "address")] + public class GovFunctionBase : FunctionMessage + { + + } + + public partial class IncreaseOrdersFunction : IncreaseOrdersFunctionBase { } + + [Function("increaseOrders", typeof(IncreaseOrdersOutputDTO))] + public class IncreaseOrdersFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + [Parameter("uint256", "", 2)] + public virtual BigInteger ReturnValue2 { get; set; } + } + + public partial class IncreaseOrdersIndexFunction : IncreaseOrdersIndexFunctionBase { } + + [Function("increaseOrdersIndex", "uint256")] + public class IncreaseOrdersIndexFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class InitializeFunction : InitializeFunctionBase { } + + [Function("initialize")] + public class InitializeFunctionBase : FunctionMessage + { + [Parameter("address", "_router", 1)] + public virtual string Router { get; set; } + [Parameter("address", "_vault", 2)] + public virtual string Vault { get; set; } + [Parameter("address", "_weth", 3)] + public virtual string Weth { get; set; } + [Parameter("address", "_usdg", 4)] + public virtual string Usdg { get; set; } + [Parameter("uint256", "_minExecutionFee", 5)] + public virtual BigInteger MinExecutionFee { get; set; } + [Parameter("uint256", "_minPurchaseTokenAmountUsd", 6)] + public virtual BigInteger MinPurchaseTokenAmountUsd { get; set; } + } + + public partial class IsInitializedFunction : IsInitializedFunctionBase { } + + [Function("isInitialized", "bool")] + public class IsInitializedFunctionBase : FunctionMessage + { + + } + + public partial class MinExecutionFeeFunction : MinExecutionFeeFunctionBase { } + + [Function("minExecutionFee", "uint256")] + public class MinExecutionFeeFunctionBase : FunctionMessage + { + + } + + public partial class MinPurchaseTokenAmountUsdFunction : MinPurchaseTokenAmountUsdFunctionBase { } + + [Function("minPurchaseTokenAmountUsd", "uint256")] + public class MinPurchaseTokenAmountUsdFunctionBase : FunctionMessage + { + + } + + public partial class RouterFunction : RouterFunctionBase { } + + [Function("router", "address")] + public class RouterFunctionBase : FunctionMessage + { + + } + + public partial class SetGovFunction : SetGovFunctionBase { } + + [Function("setGov")] + public class SetGovFunctionBase : FunctionMessage + { + [Parameter("address", "_gov", 1)] + public virtual string Gov { get; set; } + } + + public partial class SetMinExecutionFeeFunction : SetMinExecutionFeeFunctionBase { } + + [Function("setMinExecutionFee")] + public class SetMinExecutionFeeFunctionBase : FunctionMessage + { + [Parameter("uint256", "_minExecutionFee", 1)] + public virtual BigInteger MinExecutionFee { get; set; } + } + + public partial class SetMinPurchaseTokenAmountUsdFunction : SetMinPurchaseTokenAmountUsdFunctionBase { } + + [Function("setMinPurchaseTokenAmountUsd")] + public class SetMinPurchaseTokenAmountUsdFunctionBase : FunctionMessage + { + [Parameter("uint256", "_minPurchaseTokenAmountUsd", 1)] + public virtual BigInteger MinPurchaseTokenAmountUsd { get; set; } + } + + public partial class SwapOrdersFunction : SwapOrdersFunctionBase { } + + [Function("swapOrders", typeof(SwapOrdersOutputDTO))] + public class SwapOrdersFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + [Parameter("uint256", "", 2)] + public virtual BigInteger ReturnValue2 { get; set; } + } + + public partial class SwapOrdersIndexFunction : SwapOrdersIndexFunctionBase { } + + [Function("swapOrdersIndex", "uint256")] + public class SwapOrdersIndexFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class UpdateDecreaseOrderFunction : UpdateDecreaseOrderFunctionBase { } + + [Function("updateDecreaseOrder")] + public class UpdateDecreaseOrderFunctionBase : FunctionMessage + { + [Parameter("uint256", "_orderIndex", 1)] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("uint256", "_collateralDelta", 2)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "_sizeDelta", 3)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("uint256", "_triggerPrice", 4)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "_triggerAboveThreshold", 5)] + public virtual bool TriggerAboveThreshold { get; set; } + } + + public partial class UpdateIncreaseOrderFunction : UpdateIncreaseOrderFunctionBase { } + + [Function("updateIncreaseOrder")] + public class UpdateIncreaseOrderFunctionBase : FunctionMessage + { + [Parameter("uint256", "_orderIndex", 1)] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("uint256", "_sizeDelta", 2)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("uint256", "_triggerPrice", 3)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "_triggerAboveThreshold", 4)] + public virtual bool TriggerAboveThreshold { get; set; } + } + + public partial class UpdateSwapOrderFunction : UpdateSwapOrderFunctionBase { } + + [Function("updateSwapOrder")] + public class UpdateSwapOrderFunctionBase : FunctionMessage + { + [Parameter("uint256", "_orderIndex", 1)] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("uint256", "_minOut", 2)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_triggerRatio", 3)] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "_triggerAboveThreshold", 4)] + public virtual bool TriggerAboveThreshold { get; set; } + } + + public partial class UsdgFunction : UsdgFunctionBase { } + + [Function("usdg", "address")] + public class UsdgFunctionBase : FunctionMessage + { + + } + + public partial class ValidatePositionOrderPriceFunction : ValidatePositionOrderPriceFunctionBase { } + + [Function("validatePositionOrderPrice", typeof(ValidatePositionOrderPriceOutputDTO))] + public class ValidatePositionOrderPriceFunctionBase : FunctionMessage + { + [Parameter("bool", "_triggerAboveThreshold", 1)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "_triggerPrice", 2)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("address", "_indexToken", 3)] + public virtual string IndexToken { get; set; } + [Parameter("bool", "_maximizePrice", 4)] + public virtual bool MaximizePrice { get; set; } + [Parameter("bool", "_raise", 5)] + public virtual bool Raise { get; set; } + } + + public partial class ValidateSwapOrderPriceWithTriggerAboveThresholdFunction : ValidateSwapOrderPriceWithTriggerAboveThresholdFunctionBase { } + + [Function("validateSwapOrderPriceWithTriggerAboveThreshold", "bool")] + public class ValidateSwapOrderPriceWithTriggerAboveThresholdFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("uint256", "_triggerRatio", 2)] + public virtual BigInteger TriggerRatio { get; set; } + } + + public partial class VaultFunction : VaultFunctionBase { } + + [Function("vault", "address")] + public class VaultFunctionBase : FunctionMessage + { + + } + + public partial class WethFunction : WethFunctionBase { } + + [Function("weth", "address")] + public class WethFunctionBase : FunctionMessage + { + + } + + public partial class CancelDecreaseOrderEventDTO : CancelDecreaseOrderEventDTOBase { } + + [Event("CancelDecreaseOrder")] + public class CancelDecreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "collateralToken", 3, false )] + public virtual string CollateralToken { get; set; } + [Parameter("uint256", "collateralDelta", 4, false )] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("address", "indexToken", 5, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 8, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 9, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 10, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class CancelIncreaseOrderEventDTO : CancelIncreaseOrderEventDTOBase { } + + [Event("CancelIncreaseOrder")] + public class CancelIncreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "purchaseToken", 3, false )] + public virtual string PurchaseToken { get; set; } + [Parameter("uint256", "purchaseTokenAmount", 4, false )] + public virtual BigInteger PurchaseTokenAmount { get; set; } + [Parameter("address", "collateralToken", 5, false )] + public virtual string CollateralToken { get; set; } + [Parameter("address", "indexToken", 6, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 7, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 8, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 9, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 10, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 11, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class CancelSwapOrderEventDTO : CancelSwapOrderEventDTOBase { } + + [Event("CancelSwapOrder")] + public class CancelSwapOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address[]", "path", 3, false )] + public virtual List Path { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "triggerRatio", 6, false )] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "triggerAboveThreshold", 7, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("bool", "shouldUnwrap", 8, false )] + public virtual bool ShouldUnwrap { get; set; } + [Parameter("uint256", "executionFee", 9, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class CreateDecreaseOrderEventDTO : CreateDecreaseOrderEventDTOBase { } + + [Event("CreateDecreaseOrder")] + public class CreateDecreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "collateralToken", 3, false )] + public virtual string CollateralToken { get; set; } + [Parameter("uint256", "collateralDelta", 4, false )] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("address", "indexToken", 5, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 8, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 9, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 10, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class CreateIncreaseOrderEventDTO : CreateIncreaseOrderEventDTOBase { } + + [Event("CreateIncreaseOrder")] + public class CreateIncreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "purchaseToken", 3, false )] + public virtual string PurchaseToken { get; set; } + [Parameter("uint256", "purchaseTokenAmount", 4, false )] + public virtual BigInteger PurchaseTokenAmount { get; set; } + [Parameter("address", "collateralToken", 5, false )] + public virtual string CollateralToken { get; set; } + [Parameter("address", "indexToken", 6, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 7, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 8, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 9, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 10, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 11, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class CreateSwapOrderEventDTO : CreateSwapOrderEventDTOBase { } + + [Event("CreateSwapOrder")] + public class CreateSwapOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address[]", "path", 3, false )] + public virtual List Path { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "triggerRatio", 6, false )] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "triggerAboveThreshold", 7, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("bool", "shouldUnwrap", 8, false )] + public virtual bool ShouldUnwrap { get; set; } + [Parameter("uint256", "executionFee", 9, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class ExecuteDecreaseOrderEventDTO : ExecuteDecreaseOrderEventDTOBase { } + + [Event("ExecuteDecreaseOrder")] + public class ExecuteDecreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "collateralToken", 3, false )] + public virtual string CollateralToken { get; set; } + [Parameter("uint256", "collateralDelta", 4, false )] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("address", "indexToken", 5, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 8, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 9, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 10, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "executionPrice", 11, false )] + public virtual BigInteger ExecutionPrice { get; set; } + } + + public partial class ExecuteIncreaseOrderEventDTO : ExecuteIncreaseOrderEventDTOBase { } + + [Event("ExecuteIncreaseOrder")] + public class ExecuteIncreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "purchaseToken", 3, false )] + public virtual string PurchaseToken { get; set; } + [Parameter("uint256", "purchaseTokenAmount", 4, false )] + public virtual BigInteger PurchaseTokenAmount { get; set; } + [Parameter("address", "collateralToken", 5, false )] + public virtual string CollateralToken { get; set; } + [Parameter("address", "indexToken", 6, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 7, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 8, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 9, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 10, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 11, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "executionPrice", 12, false )] + public virtual BigInteger ExecutionPrice { get; set; } + } + + public partial class ExecuteSwapOrderEventDTO : ExecuteSwapOrderEventDTOBase { } + + [Event("ExecuteSwapOrder")] + public class ExecuteSwapOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address[]", "path", 3, false )] + public virtual List Path { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "amountOut", 6, false )] + public virtual BigInteger AmountOut { get; set; } + [Parameter("uint256", "triggerRatio", 7, false )] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "triggerAboveThreshold", 8, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("bool", "shouldUnwrap", 9, false )] + public virtual bool ShouldUnwrap { get; set; } + [Parameter("uint256", "executionFee", 10, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class InitializeEventDTO : InitializeEventDTOBase { } + + [Event("Initialize")] + public class InitializeEventDTOBase : IEventDTO + { + [Parameter("address", "router", 1, false )] + public virtual string Router { get; set; } + [Parameter("address", "vault", 2, false )] + public virtual string Vault { get; set; } + [Parameter("address", "weth", 3, false )] + public virtual string Weth { get; set; } + [Parameter("address", "usdg", 4, false )] + public virtual string Usdg { get; set; } + [Parameter("uint256", "minExecutionFee", 5, false )] + public virtual BigInteger MinExecutionFee { get; set; } + [Parameter("uint256", "minPurchaseTokenAmountUsd", 6, false )] + public virtual BigInteger MinPurchaseTokenAmountUsd { get; set; } + } + + public partial class UpdateDecreaseOrderEventDTO : UpdateDecreaseOrderEventDTOBase { } + + [Event("UpdateDecreaseOrder")] + public class UpdateDecreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "collateralToken", 3, false )] + public virtual string CollateralToken { get; set; } + [Parameter("uint256", "collateralDelta", 4, false )] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("address", "indexToken", 5, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 8, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 9, false )] + public virtual bool TriggerAboveThreshold { get; set; } + } + + public partial class UpdateGovEventDTO : UpdateGovEventDTOBase { } + + [Event("UpdateGov")] + public class UpdateGovEventDTOBase : IEventDTO + { + [Parameter("address", "gov", 1, false )] + public virtual string Gov { get; set; } + } + + public partial class UpdateIncreaseOrderEventDTO : UpdateIncreaseOrderEventDTOBase { } + + [Event("UpdateIncreaseOrder")] + public class UpdateIncreaseOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "orderIndex", 2, false )] + public virtual BigInteger OrderIndex { get; set; } + [Parameter("address", "collateralToken", 3, false )] + public virtual string CollateralToken { get; set; } + [Parameter("address", "indexToken", 4, false )] + public virtual string IndexToken { get; set; } + [Parameter("bool", "isLong", 5, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("uint256", "triggerPrice", 7, false )] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 8, false )] + public virtual bool TriggerAboveThreshold { get; set; } + } + + public partial class UpdateMinExecutionFeeEventDTO : UpdateMinExecutionFeeEventDTOBase { } + + [Event("UpdateMinExecutionFee")] + public class UpdateMinExecutionFeeEventDTOBase : IEventDTO + { + [Parameter("uint256", "minExecutionFee", 1, false )] + public virtual BigInteger MinExecutionFee { get; set; } + } + + public partial class UpdateMinPurchaseTokenAmountUsdEventDTO : UpdateMinPurchaseTokenAmountUsdEventDTOBase { } + + [Event("UpdateMinPurchaseTokenAmountUsd")] + public class UpdateMinPurchaseTokenAmountUsdEventDTOBase : IEventDTO + { + [Parameter("uint256", "minPurchaseTokenAmountUsd", 1, false )] + public virtual BigInteger MinPurchaseTokenAmountUsd { get; set; } + } + + public partial class UpdateSwapOrderEventDTO : UpdateSwapOrderEventDTOBase { } + + [Event("UpdateSwapOrder")] + public class UpdateSwapOrderEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("uint256", "ordexIndex", 2, false )] + public virtual BigInteger OrdexIndex { get; set; } + [Parameter("address[]", "path", 3, false )] + public virtual List Path { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "triggerRatio", 6, false )] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "triggerAboveThreshold", 7, false )] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("bool", "shouldUnwrap", 8, false )] + public virtual bool ShouldUnwrap { get; set; } + [Parameter("uint256", "executionFee", 9, false )] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class PricePrecisionOutputDTO : PricePrecisionOutputDTOBase { } + + [FunctionOutput] + public class PricePrecisionOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class UsdgPrecisionOutputDTO : UsdgPrecisionOutputDTOBase { } + + [FunctionOutput] + public class UsdgPrecisionOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + + + + + + + + + + + + + + + public partial class DecreaseOrdersOutputDTO : DecreaseOrdersOutputDTOBase { } + + [FunctionOutput] + public class DecreaseOrdersOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "account", 1)] + public virtual string Account { get; set; } + [Parameter("address", "collateralToken", 2)] + public virtual string CollateralToken { get; set; } + [Parameter("uint256", "collateralDelta", 3)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("address", "indexToken", 4)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 5)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 6)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 7)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 8)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 9)] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class DecreaseOrdersIndexOutputDTO : DecreaseOrdersIndexOutputDTOBase { } + + [FunctionOutput] + public class DecreaseOrdersIndexOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + + + + + + + public partial class GetDecreaseOrderOutputDTO : GetDecreaseOrderOutputDTOBase { } + + [FunctionOutput] + public class GetDecreaseOrderOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "collateralToken", 1)] + public virtual string CollateralToken { get; set; } + [Parameter("uint256", "collateralDelta", 2)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("address", "indexToken", 3)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 6)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 7)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 8)] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class GetIncreaseOrderOutputDTO : GetIncreaseOrderOutputDTOBase { } + + [FunctionOutput] + public class GetIncreaseOrderOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "purchaseToken", 1)] + public virtual string PurchaseToken { get; set; } + [Parameter("uint256", "purchaseTokenAmount", 2)] + public virtual BigInteger PurchaseTokenAmount { get; set; } + [Parameter("address", "collateralToken", 3)] + public virtual string CollateralToken { get; set; } + [Parameter("address", "indexToken", 4)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 5)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 6)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 7)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 8)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 9)] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class GetSwapOrderOutputDTO : GetSwapOrderOutputDTOBase { } + + [FunctionOutput] + public class GetSwapOrderOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "path0", 1)] + public virtual string Path0 { get; set; } + [Parameter("address", "path1", 2)] + public virtual string Path1 { get; set; } + [Parameter("address", "path2", 3)] + public virtual string Path2 { get; set; } + [Parameter("uint256", "amountIn", 4)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "triggerRatio", 6)] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "triggerAboveThreshold", 7)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("bool", "shouldUnwrap", 8)] + public virtual bool ShouldUnwrap { get; set; } + [Parameter("uint256", "executionFee", 9)] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class GetUsdgMinPriceOutputDTO : GetUsdgMinPriceOutputDTOBase { } + + [FunctionOutput] + public class GetUsdgMinPriceOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class GovOutputDTO : GovOutputDTOBase { } + + [FunctionOutput] + public class GovOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class IncreaseOrdersOutputDTO : IncreaseOrdersOutputDTOBase { } + + [FunctionOutput] + public class IncreaseOrdersOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "account", 1)] + public virtual string Account { get; set; } + [Parameter("address", "purchaseToken", 2)] + public virtual string PurchaseToken { get; set; } + [Parameter("uint256", "purchaseTokenAmount", 3)] + public virtual BigInteger PurchaseTokenAmount { get; set; } + [Parameter("address", "collateralToken", 4)] + public virtual string CollateralToken { get; set; } + [Parameter("address", "indexToken", 5)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "sizeDelta", 6)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "triggerPrice", 8)] + public virtual BigInteger TriggerPrice { get; set; } + [Parameter("bool", "triggerAboveThreshold", 9)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("uint256", "executionFee", 10)] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class IncreaseOrdersIndexOutputDTO : IncreaseOrdersIndexOutputDTOBase { } + + [FunctionOutput] + public class IncreaseOrdersIndexOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + + + public partial class IsInitializedOutputDTO : IsInitializedOutputDTOBase { } + + [FunctionOutput] + public class IsInitializedOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bool", "", 1)] + public virtual bool ReturnValue1 { get; set; } + } + + public partial class MinExecutionFeeOutputDTO : MinExecutionFeeOutputDTOBase { } + + [FunctionOutput] + public class MinExecutionFeeOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class MinPurchaseTokenAmountUsdOutputDTO : MinPurchaseTokenAmountUsdOutputDTOBase { } + + [FunctionOutput] + public class MinPurchaseTokenAmountUsdOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class RouterOutputDTO : RouterOutputDTOBase { } + + [FunctionOutput] + public class RouterOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + + + + + + + public partial class SwapOrdersOutputDTO : SwapOrdersOutputDTOBase { } + + [FunctionOutput] + public class SwapOrdersOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "account", 1)] + public virtual string Account { get; set; } + [Parameter("uint256", "amountIn", 2)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 3)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "triggerRatio", 4)] + public virtual BigInteger TriggerRatio { get; set; } + [Parameter("bool", "triggerAboveThreshold", 5)] + public virtual bool TriggerAboveThreshold { get; set; } + [Parameter("bool", "shouldUnwrap", 6)] + public virtual bool ShouldUnwrap { get; set; } + [Parameter("uint256", "executionFee", 7)] + public virtual BigInteger ExecutionFee { get; set; } + } + + public partial class SwapOrdersIndexOutputDTO : SwapOrdersIndexOutputDTOBase { } + + [FunctionOutput] + public class SwapOrdersIndexOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + + + + + + + public partial class UsdgOutputDTO : UsdgOutputDTOBase { } + + [FunctionOutput] + public class UsdgOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class ValidatePositionOrderPriceOutputDTO : ValidatePositionOrderPriceOutputDTOBase { } + + [FunctionOutput] + public class ValidatePositionOrderPriceOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + [Parameter("bool", "", 2)] + public virtual bool ReturnValue2 { get; set; } + } + + public partial class ValidateSwapOrderPriceWithTriggerAboveThresholdOutputDTO : ValidateSwapOrderPriceWithTriggerAboveThresholdOutputDTOBase { } + + [FunctionOutput] + public class ValidateSwapOrderPriceWithTriggerAboveThresholdOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bool", "", 1)] + public virtual bool ReturnValue1 { get; set; } + } + + public partial class VaultOutputDTO : VaultOutputDTOBase { } + + [FunctionOutput] + public class VaultOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class WethOutputDTO : WethOutputDTOBase { } + + [FunctionOutput] + public class WethOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } +} diff --git a/src/Managing.Tools.ABI/OrderBook/OrderBookService.cs b/src/Managing.Tools.ABI/OrderBook/OrderBookService.cs new file mode 100644 index 0000000..115af63 --- /dev/null +++ b/src/Managing.Tools.ABI/OrderBook/OrderBookService.cs @@ -0,0 +1,859 @@ +using System.Numerics; +using Nethereum.Web3; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.Contracts.ContractHandlers; +using Managing.Tools.OrderBook.ContractDefinition; + +namespace Managing.Tools.OrderBook +{ + public partial class OrderBookService + { + public static Task DeployContractAndWaitForReceiptAsync(Web3 web3, OrderBookDeployment orderBookDeployment, CancellationTokenSource cancellationTokenSource = null) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAndWaitForReceiptAsync(orderBookDeployment, cancellationTokenSource); + } + + public static Task DeployContractAsync(Web3 web3, OrderBookDeployment orderBookDeployment) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAsync(orderBookDeployment); + } + + public static async Task DeployContractAndGetServiceAsync(Web3 web3, OrderBookDeployment orderBookDeployment, CancellationTokenSource cancellationTokenSource = null) + { + var receipt = await DeployContractAndWaitForReceiptAsync(web3, orderBookDeployment, cancellationTokenSource); + return new OrderBookService(web3, receipt.ContractAddress); + } + + protected IWeb3 Web3 { get; } + + public ContractHandler ContractHandler { get; } + + public OrderBookService(Web3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public OrderBookService(IWeb3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public Task PricePrecisionQueryAsync(PricePrecisionFunction pricePrecisionFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(pricePrecisionFunction, blockParameter); + } + + + public Task PricePrecisionQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task UsdgPrecisionQueryAsync(UsdgPrecisionFunction usdgPrecisionFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(usdgPrecisionFunction, blockParameter); + } + + + public Task UsdgPrecisionQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task CancelDecreaseOrderRequestAsync(CancelDecreaseOrderFunction cancelDecreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(cancelDecreaseOrderFunction); + } + + public Task CancelDecreaseOrderRequestAndWaitForReceiptAsync(CancelDecreaseOrderFunction cancelDecreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelDecreaseOrderFunction, cancellationToken); + } + + public Task CancelDecreaseOrderRequestAsync(BigInteger orderIndex) + { + var cancelDecreaseOrderFunction = new CancelDecreaseOrderFunction(); + cancelDecreaseOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.SendRequestAsync(cancelDecreaseOrderFunction); + } + + public Task CancelDecreaseOrderRequestAndWaitForReceiptAsync(BigInteger orderIndex, CancellationTokenSource cancellationToken = null) + { + var cancelDecreaseOrderFunction = new CancelDecreaseOrderFunction(); + cancelDecreaseOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelDecreaseOrderFunction, cancellationToken); + } + + public Task CancelIncreaseOrderRequestAsync(CancelIncreaseOrderFunction cancelIncreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(cancelIncreaseOrderFunction); + } + + public Task CancelIncreaseOrderRequestAndWaitForReceiptAsync(CancelIncreaseOrderFunction cancelIncreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelIncreaseOrderFunction, cancellationToken); + } + + public Task CancelIncreaseOrderRequestAsync(BigInteger orderIndex) + { + var cancelIncreaseOrderFunction = new CancelIncreaseOrderFunction(); + cancelIncreaseOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.SendRequestAsync(cancelIncreaseOrderFunction); + } + + public Task CancelIncreaseOrderRequestAndWaitForReceiptAsync(BigInteger orderIndex, CancellationTokenSource cancellationToken = null) + { + var cancelIncreaseOrderFunction = new CancelIncreaseOrderFunction(); + cancelIncreaseOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelIncreaseOrderFunction, cancellationToken); + } + + public Task CancelMultipleRequestAsync(CancelMultipleFunction cancelMultipleFunction) + { + return ContractHandler.SendRequestAsync(cancelMultipleFunction); + } + + public Task CancelMultipleRequestAndWaitForReceiptAsync(CancelMultipleFunction cancelMultipleFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelMultipleFunction, cancellationToken); + } + + public Task CancelMultipleRequestAsync(List swapOrderIndexes, List increaseOrderIndexes, List decreaseOrderIndexes) + { + var cancelMultipleFunction = new CancelMultipleFunction(); + cancelMultipleFunction.SwapOrderIndexes = swapOrderIndexes; + cancelMultipleFunction.IncreaseOrderIndexes = increaseOrderIndexes; + cancelMultipleFunction.DecreaseOrderIndexes = decreaseOrderIndexes; + + return ContractHandler.SendRequestAsync(cancelMultipleFunction); + } + + public Task CancelMultipleRequestAndWaitForReceiptAsync(List swapOrderIndexes, List increaseOrderIndexes, List decreaseOrderIndexes, CancellationTokenSource cancellationToken = null) + { + var cancelMultipleFunction = new CancelMultipleFunction(); + cancelMultipleFunction.SwapOrderIndexes = swapOrderIndexes; + cancelMultipleFunction.IncreaseOrderIndexes = increaseOrderIndexes; + cancelMultipleFunction.DecreaseOrderIndexes = decreaseOrderIndexes; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelMultipleFunction, cancellationToken); + } + + public Task CancelSwapOrderRequestAsync(CancelSwapOrderFunction cancelSwapOrderFunction) + { + return ContractHandler.SendRequestAsync(cancelSwapOrderFunction); + } + + public Task CancelSwapOrderRequestAndWaitForReceiptAsync(CancelSwapOrderFunction cancelSwapOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelSwapOrderFunction, cancellationToken); + } + + public Task CancelSwapOrderRequestAsync(BigInteger orderIndex) + { + var cancelSwapOrderFunction = new CancelSwapOrderFunction(); + cancelSwapOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.SendRequestAsync(cancelSwapOrderFunction); + } + + public Task CancelSwapOrderRequestAndWaitForReceiptAsync(BigInteger orderIndex, CancellationTokenSource cancellationToken = null) + { + var cancelSwapOrderFunction = new CancelSwapOrderFunction(); + cancelSwapOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelSwapOrderFunction, cancellationToken); + } + + public Task CreateDecreaseOrderRequestAsync(CreateDecreaseOrderFunction createDecreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(createDecreaseOrderFunction); + } + + public Task CreateDecreaseOrderRequestAndWaitForReceiptAsync(CreateDecreaseOrderFunction createDecreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(createDecreaseOrderFunction, cancellationToken); + } + + public Task CreateDecreaseOrderRequestAsync(string indexToken, BigInteger sizeDelta, string collateralToken, BigInteger collateralDelta, bool isLong, BigInteger triggerPrice, bool triggerAboveThreshold) + { + var createDecreaseOrderFunction = new CreateDecreaseOrderFunction(); + createDecreaseOrderFunction.IndexToken = indexToken; + createDecreaseOrderFunction.SizeDelta = sizeDelta; + createDecreaseOrderFunction.CollateralToken = collateralToken; + createDecreaseOrderFunction.CollateralDelta = collateralDelta; + createDecreaseOrderFunction.IsLong = isLong; + createDecreaseOrderFunction.TriggerPrice = triggerPrice; + createDecreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAsync(createDecreaseOrderFunction); + } + + public Task CreateDecreaseOrderRequestAndWaitForReceiptAsync(string indexToken, BigInteger sizeDelta, string collateralToken, BigInteger collateralDelta, bool isLong, BigInteger triggerPrice, bool triggerAboveThreshold, CancellationTokenSource cancellationToken = null) + { + var createDecreaseOrderFunction = new CreateDecreaseOrderFunction(); + createDecreaseOrderFunction.IndexToken = indexToken; + createDecreaseOrderFunction.SizeDelta = sizeDelta; + createDecreaseOrderFunction.CollateralToken = collateralToken; + createDecreaseOrderFunction.CollateralDelta = collateralDelta; + createDecreaseOrderFunction.IsLong = isLong; + createDecreaseOrderFunction.TriggerPrice = triggerPrice; + createDecreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(createDecreaseOrderFunction, cancellationToken); + } + + public Task CreateIncreaseOrderRequestAsync(CreateIncreaseOrderFunction createIncreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(createIncreaseOrderFunction); + } + + public Task CreateIncreaseOrderRequestAndWaitForReceiptAsync(CreateIncreaseOrderFunction createIncreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(createIncreaseOrderFunction, cancellationToken); + } + + public Task CreateIncreaseOrderRequestAsync(List path, BigInteger amountIn, string indexToken, BigInteger minOut, BigInteger sizeDelta, string collateralToken, bool isLong, BigInteger triggerPrice, bool triggerAboveThreshold, BigInteger executionFee, bool shouldWrap) + { + var createIncreaseOrderFunction = new CreateIncreaseOrderFunction(); + createIncreaseOrderFunction.Path = path; + createIncreaseOrderFunction.AmountIn = amountIn; + createIncreaseOrderFunction.IndexToken = indexToken; + createIncreaseOrderFunction.MinOut = minOut; + createIncreaseOrderFunction.SizeDelta = sizeDelta; + createIncreaseOrderFunction.CollateralToken = collateralToken; + createIncreaseOrderFunction.IsLong = isLong; + createIncreaseOrderFunction.TriggerPrice = triggerPrice; + createIncreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + createIncreaseOrderFunction.ExecutionFee = executionFee; + createIncreaseOrderFunction.ShouldWrap = shouldWrap; + + return ContractHandler.SendRequestAsync(createIncreaseOrderFunction); + } + + public Task CreateIncreaseOrderRequestAndWaitForReceiptAsync(List path, BigInteger amountIn, string indexToken, BigInteger minOut, BigInteger sizeDelta, string collateralToken, bool isLong, BigInteger triggerPrice, bool triggerAboveThreshold, BigInteger executionFee, bool shouldWrap, CancellationTokenSource cancellationToken = null) + { + var createIncreaseOrderFunction = new CreateIncreaseOrderFunction(); + createIncreaseOrderFunction.Path = path; + createIncreaseOrderFunction.AmountIn = amountIn; + createIncreaseOrderFunction.IndexToken = indexToken; + createIncreaseOrderFunction.MinOut = minOut; + createIncreaseOrderFunction.SizeDelta = sizeDelta; + createIncreaseOrderFunction.CollateralToken = collateralToken; + createIncreaseOrderFunction.IsLong = isLong; + createIncreaseOrderFunction.TriggerPrice = triggerPrice; + createIncreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + createIncreaseOrderFunction.ExecutionFee = executionFee; + createIncreaseOrderFunction.ShouldWrap = shouldWrap; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(createIncreaseOrderFunction, cancellationToken); + } + + public Task CreateSwapOrderRequestAsync(CreateSwapOrderFunction createSwapOrderFunction) + { + return ContractHandler.SendRequestAsync(createSwapOrderFunction); + } + + public Task CreateSwapOrderRequestAndWaitForReceiptAsync(CreateSwapOrderFunction createSwapOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(createSwapOrderFunction, cancellationToken); + } + + public Task CreateSwapOrderRequestAsync(List path, BigInteger amountIn, BigInteger minOut, BigInteger triggerRatio, bool triggerAboveThreshold, BigInteger executionFee, bool shouldWrap, bool shouldUnwrap) + { + var createSwapOrderFunction = new CreateSwapOrderFunction(); + createSwapOrderFunction.Path = path; + createSwapOrderFunction.AmountIn = amountIn; + createSwapOrderFunction.MinOut = minOut; + createSwapOrderFunction.TriggerRatio = triggerRatio; + createSwapOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + createSwapOrderFunction.ExecutionFee = executionFee; + createSwapOrderFunction.ShouldWrap = shouldWrap; + createSwapOrderFunction.ShouldUnwrap = shouldUnwrap; + + return ContractHandler.SendRequestAsync(createSwapOrderFunction); + } + + public Task CreateSwapOrderRequestAndWaitForReceiptAsync(List path, BigInteger amountIn, BigInteger minOut, BigInteger triggerRatio, bool triggerAboveThreshold, BigInteger executionFee, bool shouldWrap, bool shouldUnwrap, CancellationTokenSource cancellationToken = null) + { + var createSwapOrderFunction = new CreateSwapOrderFunction(); + createSwapOrderFunction.Path = path; + createSwapOrderFunction.AmountIn = amountIn; + createSwapOrderFunction.MinOut = minOut; + createSwapOrderFunction.TriggerRatio = triggerRatio; + createSwapOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + createSwapOrderFunction.ExecutionFee = executionFee; + createSwapOrderFunction.ShouldWrap = shouldWrap; + createSwapOrderFunction.ShouldUnwrap = shouldUnwrap; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(createSwapOrderFunction, cancellationToken); + } + + public Task DecreaseOrdersQueryAsync(DecreaseOrdersFunction decreaseOrdersFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(decreaseOrdersFunction, blockParameter); + } + + public Task DecreaseOrdersQueryAsync(string returnValue1, BigInteger returnValue2, BlockParameter blockParameter = null) + { + var decreaseOrdersFunction = new DecreaseOrdersFunction(); + decreaseOrdersFunction.ReturnValue1 = returnValue1; + decreaseOrdersFunction.ReturnValue2 = returnValue2; + + return ContractHandler.QueryDeserializingToObjectAsync(decreaseOrdersFunction, blockParameter); + } + + public Task DecreaseOrdersIndexQueryAsync(DecreaseOrdersIndexFunction decreaseOrdersIndexFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(decreaseOrdersIndexFunction, blockParameter); + } + + + public Task DecreaseOrdersIndexQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var decreaseOrdersIndexFunction = new DecreaseOrdersIndexFunction(); + decreaseOrdersIndexFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(decreaseOrdersIndexFunction, blockParameter); + } + + public Task ExecuteDecreaseOrderRequestAsync(ExecuteDecreaseOrderFunction executeDecreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(executeDecreaseOrderFunction); + } + + public Task ExecuteDecreaseOrderRequestAndWaitForReceiptAsync(ExecuteDecreaseOrderFunction executeDecreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeDecreaseOrderFunction, cancellationToken); + } + + public Task ExecuteDecreaseOrderRequestAsync(string address, BigInteger orderIndex, string feeReceiver) + { + var executeDecreaseOrderFunction = new ExecuteDecreaseOrderFunction(); + executeDecreaseOrderFunction.Address = address; + executeDecreaseOrderFunction.OrderIndex = orderIndex; + executeDecreaseOrderFunction.FeeReceiver = feeReceiver; + + return ContractHandler.SendRequestAsync(executeDecreaseOrderFunction); + } + + public Task ExecuteDecreaseOrderRequestAndWaitForReceiptAsync(string address, BigInteger orderIndex, string feeReceiver, CancellationTokenSource cancellationToken = null) + { + var executeDecreaseOrderFunction = new ExecuteDecreaseOrderFunction(); + executeDecreaseOrderFunction.Address = address; + executeDecreaseOrderFunction.OrderIndex = orderIndex; + executeDecreaseOrderFunction.FeeReceiver = feeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeDecreaseOrderFunction, cancellationToken); + } + + public Task ExecuteIncreaseOrderRequestAsync(ExecuteIncreaseOrderFunction executeIncreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(executeIncreaseOrderFunction); + } + + public Task ExecuteIncreaseOrderRequestAndWaitForReceiptAsync(ExecuteIncreaseOrderFunction executeIncreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeIncreaseOrderFunction, cancellationToken); + } + + public Task ExecuteIncreaseOrderRequestAsync(string address, BigInteger orderIndex, string feeReceiver) + { + var executeIncreaseOrderFunction = new ExecuteIncreaseOrderFunction(); + executeIncreaseOrderFunction.Address = address; + executeIncreaseOrderFunction.OrderIndex = orderIndex; + executeIncreaseOrderFunction.FeeReceiver = feeReceiver; + + return ContractHandler.SendRequestAsync(executeIncreaseOrderFunction); + } + + public Task ExecuteIncreaseOrderRequestAndWaitForReceiptAsync(string address, BigInteger orderIndex, string feeReceiver, CancellationTokenSource cancellationToken = null) + { + var executeIncreaseOrderFunction = new ExecuteIncreaseOrderFunction(); + executeIncreaseOrderFunction.Address = address; + executeIncreaseOrderFunction.OrderIndex = orderIndex; + executeIncreaseOrderFunction.FeeReceiver = feeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeIncreaseOrderFunction, cancellationToken); + } + + public Task ExecuteSwapOrderRequestAsync(ExecuteSwapOrderFunction executeSwapOrderFunction) + { + return ContractHandler.SendRequestAsync(executeSwapOrderFunction); + } + + public Task ExecuteSwapOrderRequestAndWaitForReceiptAsync(ExecuteSwapOrderFunction executeSwapOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeSwapOrderFunction, cancellationToken); + } + + public Task ExecuteSwapOrderRequestAsync(string account, BigInteger orderIndex, string feeReceiver) + { + var executeSwapOrderFunction = new ExecuteSwapOrderFunction(); + executeSwapOrderFunction.Account = account; + executeSwapOrderFunction.OrderIndex = orderIndex; + executeSwapOrderFunction.FeeReceiver = feeReceiver; + + return ContractHandler.SendRequestAsync(executeSwapOrderFunction); + } + + public Task ExecuteSwapOrderRequestAndWaitForReceiptAsync(string account, BigInteger orderIndex, string feeReceiver, CancellationTokenSource cancellationToken = null) + { + var executeSwapOrderFunction = new ExecuteSwapOrderFunction(); + executeSwapOrderFunction.Account = account; + executeSwapOrderFunction.OrderIndex = orderIndex; + executeSwapOrderFunction.FeeReceiver = feeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeSwapOrderFunction, cancellationToken); + } + + public Task GetDecreaseOrderQueryAsync(GetDecreaseOrderFunction getDecreaseOrderFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getDecreaseOrderFunction, blockParameter); + } + + public Task GetDecreaseOrderQueryAsync(string account, BigInteger orderIndex, BlockParameter blockParameter = null) + { + var getDecreaseOrderFunction = new GetDecreaseOrderFunction(); + getDecreaseOrderFunction.Account = account; + getDecreaseOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.QueryDeserializingToObjectAsync(getDecreaseOrderFunction, blockParameter); + } + + public Task GetIncreaseOrderQueryAsync(GetIncreaseOrderFunction getIncreaseOrderFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getIncreaseOrderFunction, blockParameter); + } + + public Task GetIncreaseOrderQueryAsync(string account, BigInteger orderIndex, BlockParameter blockParameter = null) + { + var getIncreaseOrderFunction = new GetIncreaseOrderFunction(); + getIncreaseOrderFunction.Account = account; + getIncreaseOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.QueryDeserializingToObjectAsync(getIncreaseOrderFunction, blockParameter); + } + + public Task GetSwapOrderQueryAsync(GetSwapOrderFunction getSwapOrderFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getSwapOrderFunction, blockParameter); + } + + public Task GetSwapOrderQueryAsync(string account, BigInteger orderIndex, BlockParameter blockParameter = null) + { + var getSwapOrderFunction = new GetSwapOrderFunction(); + getSwapOrderFunction.Account = account; + getSwapOrderFunction.OrderIndex = orderIndex; + + return ContractHandler.QueryDeserializingToObjectAsync(getSwapOrderFunction, blockParameter); + } + + public Task GetUsdgMinPriceQueryAsync(GetUsdgMinPriceFunction getUsdgMinPriceFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(getUsdgMinPriceFunction, blockParameter); + } + + + public Task GetUsdgMinPriceQueryAsync(string otherToken, BlockParameter blockParameter = null) + { + var getUsdgMinPriceFunction = new GetUsdgMinPriceFunction(); + getUsdgMinPriceFunction.OtherToken = otherToken; + + return ContractHandler.QueryAsync(getUsdgMinPriceFunction, blockParameter); + } + + public Task GovQueryAsync(GovFunction govFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(govFunction, blockParameter); + } + + + public Task GovQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task IncreaseOrdersQueryAsync(IncreaseOrdersFunction increaseOrdersFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(increaseOrdersFunction, blockParameter); + } + + public Task IncreaseOrdersQueryAsync(string returnValue1, BigInteger returnValue2, BlockParameter blockParameter = null) + { + var increaseOrdersFunction = new IncreaseOrdersFunction(); + increaseOrdersFunction.ReturnValue1 = returnValue1; + increaseOrdersFunction.ReturnValue2 = returnValue2; + + return ContractHandler.QueryDeserializingToObjectAsync(increaseOrdersFunction, blockParameter); + } + + public Task IncreaseOrdersIndexQueryAsync(IncreaseOrdersIndexFunction increaseOrdersIndexFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(increaseOrdersIndexFunction, blockParameter); + } + + + public Task IncreaseOrdersIndexQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var increaseOrdersIndexFunction = new IncreaseOrdersIndexFunction(); + increaseOrdersIndexFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(increaseOrdersIndexFunction, blockParameter); + } + + public Task InitializeRequestAsync(InitializeFunction initializeFunction) + { + return ContractHandler.SendRequestAsync(initializeFunction); + } + + public Task InitializeRequestAndWaitForReceiptAsync(InitializeFunction initializeFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(initializeFunction, cancellationToken); + } + + public Task InitializeRequestAsync(string router, string vault, string weth, string usdg, BigInteger minExecutionFee, BigInteger minPurchaseTokenAmountUsd) + { + var initializeFunction = new InitializeFunction(); + initializeFunction.Router = router; + initializeFunction.Vault = vault; + initializeFunction.Weth = weth; + initializeFunction.Usdg = usdg; + initializeFunction.MinExecutionFee = minExecutionFee; + initializeFunction.MinPurchaseTokenAmountUsd = minPurchaseTokenAmountUsd; + + return ContractHandler.SendRequestAsync(initializeFunction); + } + + public Task InitializeRequestAndWaitForReceiptAsync(string router, string vault, string weth, string usdg, BigInteger minExecutionFee, BigInteger minPurchaseTokenAmountUsd, CancellationTokenSource cancellationToken = null) + { + var initializeFunction = new InitializeFunction(); + initializeFunction.Router = router; + initializeFunction.Vault = vault; + initializeFunction.Weth = weth; + initializeFunction.Usdg = usdg; + initializeFunction.MinExecutionFee = minExecutionFee; + initializeFunction.MinPurchaseTokenAmountUsd = minPurchaseTokenAmountUsd; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(initializeFunction, cancellationToken); + } + + public Task IsInitializedQueryAsync(IsInitializedFunction isInitializedFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(isInitializedFunction, blockParameter); + } + + + public Task IsInitializedQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task MinExecutionFeeQueryAsync(MinExecutionFeeFunction minExecutionFeeFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(minExecutionFeeFunction, blockParameter); + } + + + public Task MinExecutionFeeQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task MinPurchaseTokenAmountUsdQueryAsync(MinPurchaseTokenAmountUsdFunction minPurchaseTokenAmountUsdFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(minPurchaseTokenAmountUsdFunction, blockParameter); + } + + + public Task MinPurchaseTokenAmountUsdQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task RouterQueryAsync(RouterFunction routerFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(routerFunction, blockParameter); + } + + + public Task RouterQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task SetGovRequestAsync(SetGovFunction setGovFunction) + { + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(SetGovFunction setGovFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + + public Task SetGovRequestAsync(string gov) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(string gov, CancellationTokenSource cancellationToken = null) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + + public Task SetMinExecutionFeeRequestAsync(SetMinExecutionFeeFunction setMinExecutionFeeFunction) + { + return ContractHandler.SendRequestAsync(setMinExecutionFeeFunction); + } + + public Task SetMinExecutionFeeRequestAndWaitForReceiptAsync(SetMinExecutionFeeFunction setMinExecutionFeeFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMinExecutionFeeFunction, cancellationToken); + } + + public Task SetMinExecutionFeeRequestAsync(BigInteger minExecutionFee) + { + var setMinExecutionFeeFunction = new SetMinExecutionFeeFunction(); + setMinExecutionFeeFunction.MinExecutionFee = minExecutionFee; + + return ContractHandler.SendRequestAsync(setMinExecutionFeeFunction); + } + + public Task SetMinExecutionFeeRequestAndWaitForReceiptAsync(BigInteger minExecutionFee, CancellationTokenSource cancellationToken = null) + { + var setMinExecutionFeeFunction = new SetMinExecutionFeeFunction(); + setMinExecutionFeeFunction.MinExecutionFee = minExecutionFee; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMinExecutionFeeFunction, cancellationToken); + } + + public Task SetMinPurchaseTokenAmountUsdRequestAsync(SetMinPurchaseTokenAmountUsdFunction setMinPurchaseTokenAmountUsdFunction) + { + return ContractHandler.SendRequestAsync(setMinPurchaseTokenAmountUsdFunction); + } + + public Task SetMinPurchaseTokenAmountUsdRequestAndWaitForReceiptAsync(SetMinPurchaseTokenAmountUsdFunction setMinPurchaseTokenAmountUsdFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMinPurchaseTokenAmountUsdFunction, cancellationToken); + } + + public Task SetMinPurchaseTokenAmountUsdRequestAsync(BigInteger minPurchaseTokenAmountUsd) + { + var setMinPurchaseTokenAmountUsdFunction = new SetMinPurchaseTokenAmountUsdFunction(); + setMinPurchaseTokenAmountUsdFunction.MinPurchaseTokenAmountUsd = minPurchaseTokenAmountUsd; + + return ContractHandler.SendRequestAsync(setMinPurchaseTokenAmountUsdFunction); + } + + public Task SetMinPurchaseTokenAmountUsdRequestAndWaitForReceiptAsync(BigInteger minPurchaseTokenAmountUsd, CancellationTokenSource cancellationToken = null) + { + var setMinPurchaseTokenAmountUsdFunction = new SetMinPurchaseTokenAmountUsdFunction(); + setMinPurchaseTokenAmountUsdFunction.MinPurchaseTokenAmountUsd = minPurchaseTokenAmountUsd; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMinPurchaseTokenAmountUsdFunction, cancellationToken); + } + + public Task SwapOrdersQueryAsync(SwapOrdersFunction swapOrdersFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(swapOrdersFunction, blockParameter); + } + + public Task SwapOrdersQueryAsync(string returnValue1, BigInteger returnValue2, BlockParameter blockParameter = null) + { + var swapOrdersFunction = new SwapOrdersFunction(); + swapOrdersFunction.ReturnValue1 = returnValue1; + swapOrdersFunction.ReturnValue2 = returnValue2; + + return ContractHandler.QueryDeserializingToObjectAsync(swapOrdersFunction, blockParameter); + } + + public Task SwapOrdersIndexQueryAsync(SwapOrdersIndexFunction swapOrdersIndexFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(swapOrdersIndexFunction, blockParameter); + } + + + public Task SwapOrdersIndexQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var swapOrdersIndexFunction = new SwapOrdersIndexFunction(); + swapOrdersIndexFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(swapOrdersIndexFunction, blockParameter); + } + + public Task UpdateDecreaseOrderRequestAsync(UpdateDecreaseOrderFunction updateDecreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(updateDecreaseOrderFunction); + } + + public Task UpdateDecreaseOrderRequestAndWaitForReceiptAsync(UpdateDecreaseOrderFunction updateDecreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(updateDecreaseOrderFunction, cancellationToken); + } + + public Task UpdateDecreaseOrderRequestAsync(BigInteger orderIndex, BigInteger collateralDelta, BigInteger sizeDelta, BigInteger triggerPrice, bool triggerAboveThreshold) + { + var updateDecreaseOrderFunction = new UpdateDecreaseOrderFunction(); + updateDecreaseOrderFunction.OrderIndex = orderIndex; + updateDecreaseOrderFunction.CollateralDelta = collateralDelta; + updateDecreaseOrderFunction.SizeDelta = sizeDelta; + updateDecreaseOrderFunction.TriggerPrice = triggerPrice; + updateDecreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAsync(updateDecreaseOrderFunction); + } + + public Task UpdateDecreaseOrderRequestAndWaitForReceiptAsync(BigInteger orderIndex, BigInteger collateralDelta, BigInteger sizeDelta, BigInteger triggerPrice, bool triggerAboveThreshold, CancellationTokenSource cancellationToken = null) + { + var updateDecreaseOrderFunction = new UpdateDecreaseOrderFunction(); + updateDecreaseOrderFunction.OrderIndex = orderIndex; + updateDecreaseOrderFunction.CollateralDelta = collateralDelta; + updateDecreaseOrderFunction.SizeDelta = sizeDelta; + updateDecreaseOrderFunction.TriggerPrice = triggerPrice; + updateDecreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(updateDecreaseOrderFunction, cancellationToken); + } + + public Task UpdateIncreaseOrderRequestAsync(UpdateIncreaseOrderFunction updateIncreaseOrderFunction) + { + return ContractHandler.SendRequestAsync(updateIncreaseOrderFunction); + } + + public Task UpdateIncreaseOrderRequestAndWaitForReceiptAsync(UpdateIncreaseOrderFunction updateIncreaseOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(updateIncreaseOrderFunction, cancellationToken); + } + + public Task UpdateIncreaseOrderRequestAsync(BigInteger orderIndex, BigInteger sizeDelta, BigInteger triggerPrice, bool triggerAboveThreshold) + { + var updateIncreaseOrderFunction = new UpdateIncreaseOrderFunction(); + updateIncreaseOrderFunction.OrderIndex = orderIndex; + updateIncreaseOrderFunction.SizeDelta = sizeDelta; + updateIncreaseOrderFunction.TriggerPrice = triggerPrice; + updateIncreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAsync(updateIncreaseOrderFunction); + } + + public Task UpdateIncreaseOrderRequestAndWaitForReceiptAsync(BigInteger orderIndex, BigInteger sizeDelta, BigInteger triggerPrice, bool triggerAboveThreshold, CancellationTokenSource cancellationToken = null) + { + var updateIncreaseOrderFunction = new UpdateIncreaseOrderFunction(); + updateIncreaseOrderFunction.OrderIndex = orderIndex; + updateIncreaseOrderFunction.SizeDelta = sizeDelta; + updateIncreaseOrderFunction.TriggerPrice = triggerPrice; + updateIncreaseOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(updateIncreaseOrderFunction, cancellationToken); + } + + public Task UpdateSwapOrderRequestAsync(UpdateSwapOrderFunction updateSwapOrderFunction) + { + return ContractHandler.SendRequestAsync(updateSwapOrderFunction); + } + + public Task UpdateSwapOrderRequestAndWaitForReceiptAsync(UpdateSwapOrderFunction updateSwapOrderFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(updateSwapOrderFunction, cancellationToken); + } + + public Task UpdateSwapOrderRequestAsync(BigInteger orderIndex, BigInteger minOut, BigInteger triggerRatio, bool triggerAboveThreshold) + { + var updateSwapOrderFunction = new UpdateSwapOrderFunction(); + updateSwapOrderFunction.OrderIndex = orderIndex; + updateSwapOrderFunction.MinOut = minOut; + updateSwapOrderFunction.TriggerRatio = triggerRatio; + updateSwapOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAsync(updateSwapOrderFunction); + } + + public Task UpdateSwapOrderRequestAndWaitForReceiptAsync(BigInteger orderIndex, BigInteger minOut, BigInteger triggerRatio, bool triggerAboveThreshold, CancellationTokenSource cancellationToken = null) + { + var updateSwapOrderFunction = new UpdateSwapOrderFunction(); + updateSwapOrderFunction.OrderIndex = orderIndex; + updateSwapOrderFunction.MinOut = minOut; + updateSwapOrderFunction.TriggerRatio = triggerRatio; + updateSwapOrderFunction.TriggerAboveThreshold = triggerAboveThreshold; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(updateSwapOrderFunction, cancellationToken); + } + + public Task UsdgQueryAsync(UsdgFunction usdgFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(usdgFunction, blockParameter); + } + + + public Task UsdgQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task ValidatePositionOrderPriceQueryAsync(ValidatePositionOrderPriceFunction validatePositionOrderPriceFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(validatePositionOrderPriceFunction, blockParameter); + } + + public Task ValidatePositionOrderPriceQueryAsync(bool triggerAboveThreshold, BigInteger triggerPrice, string indexToken, bool maximizePrice, bool raise, BlockParameter blockParameter = null) + { + var validatePositionOrderPriceFunction = new ValidatePositionOrderPriceFunction(); + validatePositionOrderPriceFunction.TriggerAboveThreshold = triggerAboveThreshold; + validatePositionOrderPriceFunction.TriggerPrice = triggerPrice; + validatePositionOrderPriceFunction.IndexToken = indexToken; + validatePositionOrderPriceFunction.MaximizePrice = maximizePrice; + validatePositionOrderPriceFunction.Raise = raise; + + return ContractHandler.QueryDeserializingToObjectAsync(validatePositionOrderPriceFunction, blockParameter); + } + + public Task ValidateSwapOrderPriceWithTriggerAboveThresholdQueryAsync(ValidateSwapOrderPriceWithTriggerAboveThresholdFunction validateSwapOrderPriceWithTriggerAboveThresholdFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(validateSwapOrderPriceWithTriggerAboveThresholdFunction, blockParameter); + } + + + public Task ValidateSwapOrderPriceWithTriggerAboveThresholdQueryAsync(List path, BigInteger triggerRatio, BlockParameter blockParameter = null) + { + var validateSwapOrderPriceWithTriggerAboveThresholdFunction = new ValidateSwapOrderPriceWithTriggerAboveThresholdFunction(); + validateSwapOrderPriceWithTriggerAboveThresholdFunction.Path = path; + validateSwapOrderPriceWithTriggerAboveThresholdFunction.TriggerRatio = triggerRatio; + + return ContractHandler.QueryAsync(validateSwapOrderPriceWithTriggerAboveThresholdFunction, blockParameter); + } + + public Task VaultQueryAsync(VaultFunction vaultFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(vaultFunction, blockParameter); + } + + + public Task VaultQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task WethQueryAsync(WethFunction wethFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(wethFunction, blockParameter); + } + + + public Task WethQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + } +} diff --git a/src/Managing.Tools.ABI/OrderBookReader/ContractDefinition/OrderBookReaderDefinition.cs b/src/Managing.Tools.ABI/OrderBookReader/ContractDefinition/OrderBookReaderDefinition.cs new file mode 100644 index 0000000..f2507d9 --- /dev/null +++ b/src/Managing.Tools.ABI/OrderBookReader/ContractDefinition/OrderBookReaderDefinition.cs @@ -0,0 +1,94 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Managing.Tools.OrderBookReader.ContractDefinition +{ + + + public partial class OrderBookReaderDeployment : OrderBookReaderDeploymentBase + { + public OrderBookReaderDeployment() : base(BYTECODE) { } + public OrderBookReaderDeployment(string byteCode) : base(byteCode) { } + } + + public class OrderBookReaderDeploymentBase : ContractDeploymentMessage + { + public static string BYTECODE = "608060405234801561001057600080fd5b50610e27806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80630ce933b9146100465780632e18146914610199578063c38ccd5014610253575b600080fd5b6101006004803603606081101561005c57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561008f57600080fd5b8201836020820111156100a157600080fd5b803590602001918460208302840111600160201b831117156100c257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061030d945050505050565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561014457818101518382015260200161012c565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561018357818101518382015260200161016b565b5050505090500194505050505060405180910390f35b610100600480360360608110156101af57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b8111156101e257600080fd5b8201836020820111156101f457600080fd5b803590602001918460208302840111600160201b8311171561021557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610675945050505050565b6101006004803603606081101561026957600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561029c57600080fd5b8201836020820111156102ae57600080fd5b803590602001918460208302840111600160201b831117156102cf57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610a17945050505050565b606080610318610db9565b6040518060a001604052806000815260200160008152602001866001600160a01b031681526020016005815260200160028152509050606084518260600151026001600160401b038111801561036d57600080fd5b50604051908082528060200260200182016040528015610397578160200160208202803683370190505b509050606085518360800151026001600160401b03811180156103b957600080fd5b506040519080825280602002602001820160405280156103e3578160200160208202803683370190505b509050875b865184511015610667578684600001518151811061040257fe5b60200260200101518460200181815250506000806000806000806000876001600160a01b031663026032ee8c604001518d602001516040518363ffffffff1660e01b815260040180836001600160a01b03168152602001828152602001925050506101006040518083038186803b15801561047c57600080fd5b505afa158015610490573d6000803e3d6000fd5b505050506040513d6101008110156104a757600080fd5b810190808051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190505050509650965096509650965096509650858a8c606001518d60000151028151811061052457fe5b602002602001018181525050838a8c606001518d60000151026001018151811061054a57fe5b60200260200101818152505082610562576000610565565b60015b60ff168a8c606001518d60000151026002018151811061058157fe5b602002602001018181525050818a8c606001518d6000015102600301815181106105a757fe5b602002602001018181525050806105bf5760006105c2565b60015b60ff168a8c606001518d6000015102600401815181106105de57fe5b60200260200101818152505086898c608001518d60000151028151811061060157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084898c608001518d60000151026001018151811061063b57fe5b6001600160a01b0390921660209283029190910190910152505088516001018952506103e89350505050565b509097909650945050505050565b606080610680610db9565b6040518060a001604052806000815260200160008152602001866001600160a01b031681526020016005815260200160038152509050606084518260600151026001600160401b03811180156106d557600080fd5b506040519080825280602002602001820160405280156106ff578160200160208202803683370190505b509050606085518360800151026001600160401b038111801561072157600080fd5b5060405190808252806020026020018201604052801561074b578160200160208202803683370190505b509050875b865184511015610667578684600001518151811061076a57fe5b6020026020010151846020018181525050600080600080600080600080886001600160a01b031663d0d40cd68d604001518e602001516040518363ffffffff1660e01b815260040180836001600160a01b03168152602001828152602001925050506101206040518083038186803b1580156107e557600080fd5b505afa1580156107f9573d6000803e3d6000fd5b505050506040513d61012081101561081057600080fd5b810190808051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291905050505097509750975097509750975097509750848b8d606001518e60000151028151811061089957fe5b602002602001018181525050838b8d606001518e6000015102600101815181106108bf57fe5b602002602001018181525050828b8d606001518e6000015102600201815181106108e557fe5b602002602001018181525050816108fd576000610900565b60015b60ff168b8d606001518e60000151026003018151811061091c57fe5b60200260200101818152505080610934576000610937565b60015b60ff168b8d606001518e60000151026004018151811061095357fe5b602002602001018181525050878a8d608001518e60000151028151811061097657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050868a8d608001518e6000015102600101815181106109b057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050858a8d608001518e6000015102600201815181106109ea57fe5b6001600160a01b0390921660209283029190910190910152505089516001018a5250610750945050505050565b606080610a22610db9565b6040518060a001604052806000815260200160008152602001866001600160a01b031681526020016005815260200160038152509050606084518260600151026001600160401b0381118015610a7757600080fd5b50604051908082528060200260200182016040528015610aa1578160200160208202803683370190505b509050606085518360800151026001600160401b0381118015610ac357600080fd5b50604051908082528060200260200182016040528015610aed578160200160208202803683370190505b509050875b8651845110156106675786846000015181518110610b0c57fe5b6020026020010151846020018181525050600080600080600080600080886001600160a01b031663d3bab1d18d604001518e602001516040518363ffffffff1660e01b815260040180836001600160a01b03168152602001828152602001925050506101206040518083038186803b158015610b8757600080fd5b505afa158015610b9b573d6000803e3d6000fd5b505050506040513d610120811015610bb257600080fd5b810190808051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291905050505097509750975097509750975097509750868b8d606001518e600001510281518110610c3b57fe5b602002602001018181525050838b8d606001518e600001510260010181518110610c6157fe5b60200260200101818152505082610c79576000610c7c565b60015b60ff168b8d606001518e600001510260020181518110610c9857fe5b602002602001018181525050818b8d606001518e600001510260030181518110610cbe57fe5b60200260200101818152505080610cd6576000610cd9565b60015b60ff168b8d606001518e600001510260040181518110610cf557fe5b602002602001018181525050878a8d608001518e600001510281518110610d1857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050858a8d608001518e600001510260010181518110610d5257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050848a8d608001518e600001510260020181518110610d8c57fe5b6001600160a01b0390921660209283029190910190910152505089516001018a5250610af2945050505050565b6040518060a00160405280600081526020016000815260200160006001600160a01b031681526020016000815260200160008152509056fea26469706673582212200490431a3ff46032daa47d7d2efe24198ccd39e8e57ff9d6fccef3b97ebe51b664736f6c634300060c0033"; + public OrderBookReaderDeploymentBase() : base(BYTECODE) { } + public OrderBookReaderDeploymentBase(string byteCode) : base(byteCode) { } + + } + + public partial class GetDecreaseOrdersFunction : GetDecreaseOrdersFunctionBase { } + + [Function("getDecreaseOrders", typeof(GetDecreaseOrdersOutputDTO))] + public class GetDecreaseOrdersFunctionBase : FunctionMessage + { + [Parameter("address", "_orderBookAddress", 1)] + public virtual string OrderBookAddress { get; set; } + [Parameter("address", "_account", 2)] + public virtual string Account { get; set; } + [Parameter("uint256[]", "_indices", 3)] + public virtual List Indices { get; set; } + } + + public partial class GetIncreaseOrdersFunction : GetIncreaseOrdersFunctionBase { } + + [Function("getIncreaseOrders", typeof(GetIncreaseOrdersOutputDTO))] + public class GetIncreaseOrdersFunctionBase : FunctionMessage + { + [Parameter("address", "_orderBookAddress", 1)] + public virtual string OrderBookAddress { get; set; } + [Parameter("address", "_account", 2)] + public virtual string Account { get; set; } + [Parameter("uint256[]", "_indices", 3)] + public virtual List Indices { get; set; } + } + + public partial class GetSwapOrdersFunction : GetSwapOrdersFunctionBase { } + + [Function("getSwapOrders", typeof(GetSwapOrdersOutputDTO))] + public class GetSwapOrdersFunctionBase : FunctionMessage + { + [Parameter("address", "_orderBookAddress", 1)] + public virtual string OrderBookAddress { get; set; } + [Parameter("address", "_account", 2)] + public virtual string Account { get; set; } + [Parameter("uint256[]", "_indices", 3)] + public virtual List Indices { get; set; } + } + + public partial class GetDecreaseOrdersOutputDTO : GetDecreaseOrdersOutputDTOBase { } + + [FunctionOutput] + public class GetDecreaseOrdersOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + [Parameter("address[]", "", 2)] + public virtual List ReturnValue2 { get; set; } + } + + public partial class GetIncreaseOrdersOutputDTO : GetIncreaseOrdersOutputDTOBase { } + + [FunctionOutput] + public class GetIncreaseOrdersOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + [Parameter("address[]", "", 2)] + public virtual List ReturnValue2 { get; set; } + } + + public partial class GetSwapOrdersOutputDTO : GetSwapOrdersOutputDTOBase { } + + [FunctionOutput] + public class GetSwapOrdersOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + [Parameter("address[]", "", 2)] + public virtual List ReturnValue2 { get; set; } + } +} diff --git a/src/Managing.Tools.ABI/OrderBookReader/OrderBookReaderService.cs b/src/Managing.Tools.ABI/OrderBookReader/OrderBookReaderService.cs new file mode 100644 index 0000000..f206d7f --- /dev/null +++ b/src/Managing.Tools.ABI/OrderBookReader/OrderBookReaderService.cs @@ -0,0 +1,88 @@ +using System.Numerics; +using Nethereum.Web3; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.Contracts.ContractHandlers; +using Managing.Tools.OrderBookReader.ContractDefinition; + +namespace Managing.Tools.OrderBookReader +{ + public partial class OrderBookReaderService + { + public static Task DeployContractAndWaitForReceiptAsync(Web3 web3, OrderBookReaderDeployment orderBookReaderDeployment, CancellationTokenSource cancellationTokenSource = null) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAndWaitForReceiptAsync(orderBookReaderDeployment, cancellationTokenSource); + } + + public static Task DeployContractAsync(Web3 web3, OrderBookReaderDeployment orderBookReaderDeployment) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAsync(orderBookReaderDeployment); + } + + public static async Task DeployContractAndGetServiceAsync(Web3 web3, OrderBookReaderDeployment orderBookReaderDeployment, CancellationTokenSource cancellationTokenSource = null) + { + var receipt = await DeployContractAndWaitForReceiptAsync(web3, orderBookReaderDeployment, cancellationTokenSource); + return new OrderBookReaderService(web3, receipt.ContractAddress); + } + + protected IWeb3 Web3 { get; } + + public ContractHandler ContractHandler { get; } + + public OrderBookReaderService(Web3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public OrderBookReaderService(IWeb3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public Task GetDecreaseOrdersQueryAsync(GetDecreaseOrdersFunction getDecreaseOrdersFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getDecreaseOrdersFunction, blockParameter); + } + + public Task GetDecreaseOrdersQueryAsync(string orderBookAddress, string account, List indices, BlockParameter blockParameter = null) + { + var getDecreaseOrdersFunction = new GetDecreaseOrdersFunction(); + getDecreaseOrdersFunction.OrderBookAddress = orderBookAddress; + getDecreaseOrdersFunction.Account = account; + getDecreaseOrdersFunction.Indices = indices; + + return ContractHandler.QueryDeserializingToObjectAsync(getDecreaseOrdersFunction, blockParameter); + } + + public Task GetIncreaseOrdersQueryAsync(GetIncreaseOrdersFunction getIncreaseOrdersFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getIncreaseOrdersFunction, blockParameter); + } + + public Task GetIncreaseOrdersQueryAsync(string orderBookAddress, string account, List indices, BlockParameter blockParameter = null) + { + var getIncreaseOrdersFunction = new GetIncreaseOrdersFunction(); + getIncreaseOrdersFunction.OrderBookAddress = orderBookAddress; + getIncreaseOrdersFunction.Account = account; + getIncreaseOrdersFunction.Indices = indices; + + return ContractHandler.QueryDeserializingToObjectAsync(getIncreaseOrdersFunction, blockParameter); + } + + public Task GetSwapOrdersQueryAsync(GetSwapOrdersFunction getSwapOrdersFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getSwapOrdersFunction, blockParameter); + } + + public Task GetSwapOrdersQueryAsync(string orderBookAddress, string account, List indices, BlockParameter blockParameter = null) + { + var getSwapOrdersFunction = new GetSwapOrdersFunction(); + getSwapOrdersFunction.OrderBookAddress = orderBookAddress; + getSwapOrdersFunction.Account = account; + getSwapOrdersFunction.Indices = indices; + + return ContractHandler.QueryDeserializingToObjectAsync(getSwapOrdersFunction, blockParameter); + } + } +} diff --git a/src/Managing.Tools.ABI/PositionRouter/ContractDefinition/PositionRouterDefinition.cs b/src/Managing.Tools.ABI/PositionRouter/ContractDefinition/PositionRouterDefinition.cs new file mode 100644 index 0000000..d7ddf6a --- /dev/null +++ b/src/Managing.Tools.ABI/PositionRouter/ContractDefinition/PositionRouterDefinition.cs @@ -0,0 +1,1353 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Managing.Tools.PositionRouter.ContractDefinition +{ + + + public partial class PositionRouterDeployment : PositionRouterDeploymentBase + { + public PositionRouterDeployment() : base(BYTECODE) { } + public PositionRouterDeployment(string byteCode) : base(byteCode) { } + } + + public class PositionRouterDeploymentBase : ContractDeploymentMessage + { + public static string BYTECODE = "608060405260646008556011805460ff191660011790553480156200002357600080fd5b50604051620060bb380380620060bb833981810160405260c08110156200004957600080fd5b508051602082015160408301516060840151608085015160a090950151600160008181558154336001600160a01b031991821681179093556003805482166001600160a01b03998a16179055600580548216978916979097179096556006805487169588169590951790945560079690965560048054851695909216949094179055600280549092169093179055600d55615fd0908190620000eb90396000f3fe60806040526004361061027e5760003560e01c80626cc35e146102d357806304225954146103045780630d4d003d146103405780631045c74e1461038d578063126082cf146103c057806312d43a51146103d55780631bca8cf0146103ea5780631ce9cb8f146103ff5780631f28510614610432578063225fc9fd146104d4578063233bfe3b1461050d57806324a084df1461053757806324f746971461057057806327b42c0f14610585578063308aa81f146105be5780633422ead1146105ee57806336eba48a146106295780633a2a80c71461065c5780633e72a262146106715780633fc8cef3146106865780634067b1321461069b5780634278555f146106d1578063490ae210146106fb5780635841fcaa146107255780635b88e8c61461073a5780635d5c22e81461081357806360a362e21461088d57806362f8a3fe146108c6578063633451de146108ff57806363ae210314610932578063657bc5d01461094757806367a527931461095c578063704b6c02146109715780637be7d141146109a45780637c2eb9f714610a8f5780638a54942f14610abb57806395e9bbd714610ae55780639698d25a14610b0f57806398d1e03a14610b425780639a20810014610b575780639b57862014610b90578063ae4d7f9a14610ba5578063cb0269c914610bd8578063cfad57a214610bed578063e1f21c6714610c20578063ef12c67e14610c63578063f255527814610e15578063f2ae372f14610e50578063f2cea6a514610f2f578063f3883d8b14610f6a578063f851a44014610fa3578063f887ea4014610fb8578063fa44457714610fcd578063faf990f314611000578063fbfa77cf14611099578063fc2cee62146110ae576102ce565b366102ce576006546001600160a01b031633146102cc5760405162461bcd60e51b8152600401808060200182810382526023815260200180615f4e6023913960400191505060405180910390fd5b005b600080fd5b3480156102df57600080fd5b506102e86110d8565b604080516001600160a01b039092168252519081900360200190f35b34801561031057600080fd5b5061032e6004803603602081101561032757600080fd5b50356110e7565b60408051918252519081900360200190f35b34801561034c57600080fd5b506103796004803603604081101561036357600080fd5b50803590602001356001600160a01b0316611105565b604080519115158252519081900360200190f35b34801561039957600080fd5b5061032e600480360360208110156103b057600080fd5b50356001600160a01b03166115a3565b3480156103cc57600080fd5b5061032e6115b5565b3480156103e157600080fd5b506102e86115bb565b3480156103f657600080fd5b5061032e6115ca565b34801561040b57600080fd5b5061032e6004803603602081101561042257600080fd5b50356001600160a01b03166115d0565b34801561043e57600080fd5b5061045c6004803603602081101561045557600080fd5b50356115e2565b604080516001600160a01b039e8f1681529c8e1660208e01528c81019b909b5260608c019990995296151560808b0152948a1660a08a015260c089019390935260e088019190915261010087015261012086015261014085015215156101608401529092166101808201529051908190036101a00190f35b3480156104e057600080fd5b50610379600480360360408110156104f757600080fd5b50803590602001356001600160a01b0316611655565b34801561051957600080fd5b506102cc6004803603602081101561053057600080fd5b5035611a1f565b34801561054357600080fd5b506102cc6004803603604081101561055a57600080fd5b506001600160a01b038135169060200135611aa7565b34801561057c57600080fd5b5061032e611b0b565b34801561059157600080fd5b50610379600480360360408110156105a857600080fd5b50803590602001356001600160a01b0316611b11565b3480156105ca57600080fd5b506102cc600480360360408110156105e157600080fd5b5080359060200135611f73565b3480156105fa57600080fd5b506102cc6004803603604081101561061157600080fd5b506001600160a01b0381351690602001351515612009565b34801561063557600080fd5b506103796004803603602081101561064c57600080fd5b50356001600160a01b03166120b6565b34801561066857600080fd5b5061032e6120cb565b34801561067d57600080fd5b506103796120d1565b34801561069257600080fd5b506102e86120da565b3480156106a757600080fd5b506102cc600480360360608110156106be57600080fd5b50803590602081013590604001356120e9565b3480156106dd57600080fd5b5061032e600480360360208110156106f457600080fd5b503561218a565b34801561070757600080fd5b506102cc6004803603602081101561071e57600080fd5b5035612197565b34801561073157600080fd5b5061032e61221f565b61032e600480360361012081101561075157600080fd5b810190602081018135600160201b81111561076b57600080fd5b82018360208201111561077d57600080fd5b803590602001918460208302840111600160201b8311171561079e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b03908116935060208301359260408101359250606081013515159160808201359160a08101359160c08201359160e0013516612225565b34801561081f57600080fd5b5061083d6004803603602081101561083657600080fd5b50356123e1565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610879578181015183820152602001610861565b505050509050019250505060405180910390f35b34801561089957600080fd5b50610379600480360360408110156108b057600080fd5b50803590602001356001600160a01b0316612517565b3480156108d257600080fd5b5061032e600480360360408110156108e957600080fd5b506001600160a01b0381351690602001356128b5565b34801561090b57600080fd5b5061032e6004803603602081101561092257600080fd5b50356001600160a01b03166128fb565b34801561093e57600080fd5b5061032e61290d565b34801561095357600080fd5b506102e8612913565b34801561096857600080fd5b5061032e612922565b34801561097d57600080fd5b506102cc6004803603602081101561099457600080fd5b50356001600160a01b0316612928565b61032e60048036036101608110156109bb57600080fd5b810190602081018135600160201b8111156109d557600080fd5b8201836020820111156109e757600080fd5b803590602001918460208302840111600160201b83111715610a0857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b039081169350602083013592604081013592506060810135151591608082013581169160a08101359160c08201359160e0810135916101008201351515916101200135166129c9565b348015610a9b57600080fd5b506102cc60048036036020811015610ab257600080fd5b50351515612b79565b348015610ac757600080fd5b506102cc60048036036020811015610ade57600080fd5b5035612c0d565b348015610af157600080fd5b5061083d60048036036020811015610b0857600080fd5b5035612c95565b348015610b1b57600080fd5b5061032e60048036036020811015610b3257600080fd5b50356001600160a01b0316612dc3565b348015610b4e57600080fd5b5061032e612dd5565b348015610b6357600080fd5b506102cc60048036036040811015610b7a57600080fd5b50803590602001356001600160a01b0316612ddb565b348015610b9c57600080fd5b5061032e612fb3565b348015610bb157600080fd5b506102cc60048036036020811015610bc857600080fd5b50356001600160a01b0316612fb9565b348015610be457600080fd5b5061032e61305a565b348015610bf957600080fd5b506102cc60048036036020811015610c1057600080fd5b50356001600160a01b0316613060565b348015610c2c57600080fd5b506102cc60048036036060811015610c4357600080fd5b506001600160a01b038135811691602081013590911690604001356130cf565b348015610c6f57600080fd5b506102cc60048036036060811015610c8657600080fd5b810190602081018135600160201b811115610ca057600080fd5b820183602082011115610cb257600080fd5b803590602001918460208302840111600160201b83111715610cd357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610d2257600080fd5b820183602082011115610d3457600080fd5b803590602001918460208302840111600160201b83111715610d5557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610da457600080fd5b820183602082011115610db657600080fd5b803590602001918460208302840111600160201b83111715610dd757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506131a4945050505050565b348015610e2157600080fd5b506102cc60048036036040811015610e3857600080fd5b506001600160a01b0381358116916020013516613393565b61032e6004803603610140811015610e6757600080fd5b810190602081018135600160201b811115610e8157600080fd5b820183602082011115610e9357600080fd5b803590602001918460208302840111600160201b83111715610eb457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550506001600160a01b038335811694506020840135936040810135935060608101359250608081013515159160a08201359160c08101359160e082013591610100013516613477565b348015610f3b57600080fd5b50610f4461365a565b604080519485526020850193909352838301919091526060830152519081900360800190f35b348015610f7657600080fd5b506102cc60048036036040811015610f8d57600080fd5b50803590602001356001600160a01b031661366c565b348015610faf57600080fd5b506102e8613844565b348015610fc457600080fd5b506102e8613853565b348015610fd957600080fd5b5061032e60048036036020811015610ff057600080fd5b50356001600160a01b0316613862565b34801561100c57600080fd5b5061102a6004803603602081101561102357600080fd5b5035613874565b604080516001600160a01b039d8e1681529b8d1660208d01528b81019a909a5260608b019890985260808a019690965293151560a089015260c088019290925260e087015261010086015261012085015215156101408401529092166101608201529051908190036101800190f35b3480156110a557600080fd5b506102e86138e8565b3480156110ba57600080fd5b506102cc600480360360208110156110d157600080fd5b50356138f7565b6009546001600160a01b031681565b601281815481106110f457fe5b600091825260209091200154905081565b60006002600054141561114d576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b600260005561115a615b85565b6000848152601b602090815260409182902082516101c08101845281546001600160a01b03168152600182018054855181860281018601909652808652919492938581019392908301828280156111da57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116111bc575b505050918352505060028201546001600160a01b0390811660208301526003830154604083015260048301546060830152600583015460ff8082161515608085015261010091829004831660a0850152600685015460c0850152600785015460e08501526008850154828501526009850154610120850152600a850154610140850152600b90940154938416151561016084015290920482166101809091015281519192501661128e576001915050611598565b60006112a9826101400151836101600151846000015161397f565b9050806112bb57600092505050611598565b6000858152601b6020526040812080546001600160a01b0319168155906112e56001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820180546001600160a81b031990811690915560068301829055600783018290556008830182905560098301829055600a8301829055600b909201805490921690915582516020840151805161138b929190849061136457fe5b60200260200101518560400151866060015187608001518860a00151308a60e00151613b05565b9050801561143a57600183602001515111156113fb57600354602084015180516113e4926001600160a01b03169184916000906113c457fe5b60200260200101516001600160a01b0316613f9e9092919063ffffffff16565b6113f8836020015184610100015130613ff5565b90505b8261018001511561141957611414818460c00151614073565b61143a565b61143a8360c00151828560200151600187602001515103815181106113c457fe5b61144983610120015186614073565b82600001516001600160a01b03167f21435c5b618d77ff3657140cd3318e2cffaebc5e0e1b7318f56a9ba4044c3ed284602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001518c61012001516114bf8e61014001514361410190919063ffffffff16565b6101608f01516114d0904290614101565b60405180806020018c6001600160a01b031681526020018b81526020018a81526020018915158152602001886001600160a01b0316815260200187815260200186815260200185815260200184815260200183815260200182810382528d818151815260200191508051906020019060200280838360005b83811015611560578181015183820152602001611548565b505050509050019c5050505050505050505050505060405180910390a2611590836101a001518760016000614143565b600193505050505b600160005592915050565b600b6020526000908152604090205481565b61271081565b6001546001600160a01b031681565b60155481565b600a6020526000908152604090205481565b601b602052600090815260409020805460028201546003830154600484015460058501546006860154600787015460088801546009890154600a8a0154600b909a01546001600160a01b03998a169a988a16999798969760ff80881698610100988990048316989093918216929104168d565b60006002600054141561169d576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b60026000556116aa615c3a565b60008481526019602090815260409182902082516101a08101845281546001600160a01b031681526001820180548551818602810186019096528086529194929385810193929083018282801561172a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161170c575b505050918352505060028201546001600160a01b039081166020830152600383015460408301526004830154606083015260058301546080830152600683015460ff908116151560a0840152600784015460c0840152600884015460e0840152600984015461010080850191909152600a850154610120850152600b909401549081161515610140840152929092048216610160909101528151919250166117d6576001915050611598565b60006117f18261012001518361014001518460000151614249565b90508061180357600092505050611598565b600085815260196020526040812080546001600160a01b03191681559061182d6001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820181905560068201805460ff19169055600782018190556008820181905560098201819055600a820155600b0180546001600160a81b0319169055610160820151156118b0576118ab82606001518360000151614073565b6118ce565b6118ce8260000151836060015184602001516000815181106113c457fe5b6118dd82610100015185614073565b81600001516001600160a01b03167f35b638e650e2328786fb405bd69d2083dbedc018d086662e74b775b4f1dae4bf83602001518460400151856060015186608001518760a001518860c001518960e001518a610100015161194d8c61012001514361410190919063ffffffff16565b6101408d015161195e904290614101565b60405180806020018b6001600160a01b031681526020018a8152602001898152602001888152602001871515815260200186815260200185815260200184815260200183815260200182810382528c818151815260200191508051906020019060200280838360005b838110156119df5781810151838201526020016119c7565b505050509050019b50505050505050505050505060405180910390a2611a0e8261018001518660006001614143565b600192505050600160005592915050565b6002546001600160a01b03163314611a6c576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60088190556040805182815290517f21167d0d4661af93817ebce920f18986eed3d75d5e1c03f2aed05efcbafbc4529181900360200190a150565b6001546001600160a01b03163314611af4576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b611b076001600160a01b038316826142b1565b5050565b60165481565b600060026000541415611b59576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055611b66615c3a565b60008481526019602090815260409182902082516101a08101845281546001600160a01b0316815260018201805485518186028101860190965280865291949293858101939290830182828015611be657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611bc8575b505050918352505060028201546001600160a01b039081166020830152600383015460408301526004830154606083015260058301546080830152600683015460ff908116151560a0840152600784015460c0840152600884015460e0840152600984015461010080850191909152600a850154610120850152600b90940154908116151561014084015292909204821661016090910152815191925016611c92576001915050611598565b6000611cad826101200151836101400151846000015161397f565b905080611cbf57600092505050611598565b600085815260196020526040812080546001600160a01b031916815590611ce96001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820181905560068201805460ff19169055600782018190556008820181905560098201819055600a820155600b0180546001600160a81b0319169055606082015115611df657606082015160208301515160011015611da557600354606084015160208501518051611d8f936001600160a01b03169291906000906113c457fe5b611da28360200151846080015130613ff5565b90505b6000611dc53385602001518487604001518860c001518960a00151614396565b60035460208601518051929350611df3926001600160a01b039092169184919060001981019081106113c457fe5b50505b815160208301518051611e349291906000198101908110611e1357fe5b602002602001015184604001518560a001518660c001518760e00151614475565b611e4382610100015185614073565b81600001516001600160a01b03167f1be316b94d38c07bd41cdb4913772d0a0a82802786a2f8b657b6e85dbcdfc64183602001518460400151856060015186608001518760a001518860c001518960e001518a6101000151611eb38c61012001514361410190919063ffffffff16565b6101408d0151611ec4904290614101565b60405180806020018b6001600160a01b031681526020018a8152602001898152602001888152602001871515815260200186815260200185815260200184815260200183815260200182810382528c818151815260200191508051906020019060200280838360005b83811015611f45578181015183820152602001611f2d565b505050509050019b50505050505050505050505060405180910390a2611a0e82610180015186600180614143565b6002546001600160a01b03163314611fc0576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60148290556015819055604080518381526020810183905281517febb0f666150f4be5b60c45df8f3e49992510b0128027fe58eea6110f296493bc929181900390910190a15050565b6002546001600160a01b03163314612056576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b6001600160a01b038216600081815260176020908152604091829020805460ff1916851515908117909155825190815291517ffbabc02389290a451c6e600d05bf9887b99bfad39d8e1237e4e3df042e4941fe9281900390910190a25050565b60176020526000908152604090205460ff1681565b600f5481565b60115460ff1681565b6006546001600160a01b031681565b6002546001600160a01b03163314612136576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b600e839055600f8290556010819055604080518481526020810184905280820183905290517fb98e759701eaca2e60c25e91109003c1c7442ef731b5d569037063005da8254d9181900360600190a1505050565b601381815481106110f457fe5b6002546001600160a01b031633146121e4576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60078190556040805182815290517f974fd3c1fcb4653dfc4fb740c4c692cd212d55c28f163f310128cb64d83006759181900360200190a150565b600e5481565b60006002600054141561226d576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055600d548410156122af576040805162461bcd60e51b815260206004820152600360248201526266656560e81b604482015290519081900360640190fd5b833410156122ea576040805162461bcd60e51b81526020600482015260036024820152621d985b60ea1b604482015290519081900360640190fd5b8951600114806122fb575089516002145b612332576040805162461bcd60e51b81526020600482015260036024820152623632b760e91b604482015290519081900360640190fd5b6006548a516001600160a01b03909116908b9060009061234e57fe5b60200260200101516001600160a01b03161461239a576040805162461bcd60e51b815260206004808301919091526024820152630e0c2e8d60e31b604482015290519081900360640190fd5b6123a261487d565b6123ab836148e9565b60006123b73486614101565b90506123cd338c8c848d8d8d8d8d60018d61495d565b60016000559b9a5050505050505050505050565b60606123eb615b85565b6000838152601b602090815260409182902082516101c08101845281546001600160a01b031681526001820180548551818602810186019096528086529194929385810193929083018282801561246b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161244d575b505050918352505060028201546001600160a01b039081166020808401919091526003840154604084015260048401546060840152600584015460ff8082161515608086015261010091829004841660a0860152600686015460c0860152600786015460e08601526008860154828601526009860154610120860152600a860154610140860152600b909501549485161515610160850152909304166101809091015201519392505050565b60006002600054141561255f576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b600260005561256c615b85565b6000848152601b602090815260409182902082516101c08101845281546001600160a01b03168152600182018054855181860281018601909652808652919492938581019392908301828280156125ec57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116125ce575b505050918352505060028201546001600160a01b0390811660208301526003830154604083015260048301546060830152600583015460ff8082161515608085015261010091829004831660a0850152600685015460c0850152600785015460e08501526008850154828501526009850154610120850152600a850154610140850152600b9094015493841615156101608401529092048216610180909101528151919250166126a0576001915050611598565b60006126bb8261014001518361016001518460000151614249565b9050806126cd57600092505050611598565b6000858152601b6020526040812080546001600160a01b0319168155906126f76001830182615c1c565b506002810180546001600160a01b0319169055600060038201819055600482018190556005820180546001600160a81b031990811690915560068301829055600783018290556008830182905560098301829055600a830191909155600b9091018054909116905561012082015161276f9085614073565b81600001516001600160a01b03167f87abfd78e844f28318363bdf3da99eab2f4a2da9ff7ae365484507f7b6c3f80583602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b61012001516127e58d61014001514361410190919063ffffffff16565b6101608e01516127f6904290614101565b60405180806020018c6001600160a01b031681526020018b81526020018a81526020018915158152602001886001600160a01b0316815260200187815260200186815260200185815260200184815260200183815260200182810382528d818151815260200191508051906020019060200280838360005b8381101561288657818101518382015260200161286e565b505050509050019c5050505050505050505050505060405180910390a2611a0e826101a0015186600080614143565b6000828260405160200180836001600160a01b031660601b8152601401828152602001925050506040516020818303038152906040528051906020012090505b92915050565b60186020526000908152604090205481565b600d5481565b6004546001600160a01b031681565b60075481565b6001546001600160a01b03163314612975576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b600280546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a19181900360200190a150565b600060026000541415612a11576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055600d54841015612a53576040805162461bcd60e51b815260206004820152600360248201526266656560e81b604482015290519081900360640190fd5b833414612a8d576040805162461bcd60e51b81526020600482015260036024820152621d985b60ea1b604482015290519081900360640190fd5b8b5160011480612a9e57508b516002145b612ad5576040805162461bcd60e51b81526020600482015260036024820152623632b760e91b604482015290519081900360640190fd5b8215612b48576006548c516001600160a01b03909116908d906000198101908110612afc57fe5b60200260200101516001600160a01b031614612b48576040805162461bcd60e51b815260206004808301919091526024820152630e0c2e8d60e31b604482015290519081900360640190fd5b612b5061487d565b612b64338d8d8d8d8d8d8d8d8d8d8d614af4565b60016000559c9b505050505050505050505050565b6002546001600160a01b03163314612bc6576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b6011805482151560ff19909116811790915560408051918252517f4eb87a5935d402aa24c01b45bfb30adefcd2328b480f2d967864de4b64ea929f9181900360200190a150565b6002546001600160a01b03163314612c5a576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60168190556040805182815290517f22bd2c9f980325d046be74aaef5fc76df4a2bc3fbc7c5a1200fcc79fe80dab6c9181900360200190a150565b6060612c9f615c3a565b60008381526019602090815260409182902082516101a08101845281546001600160a01b0316815260018201805485518186028101860190965280865291949293858101939290830182828015612d1f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612d01575b505050918352505060028201546001600160a01b03908116602080840191909152600384015460408401526004840154606084015260058401546080840152600684015460ff908116151560a0850152600785015460c0850152600885015460e0850152600985015461010080860191909152600a860154610120860152600b90950154908116151561014085015293909304166101609091015201519392505050565b600c6020526000908152604090205481565b60085481565b3360009081526017602052604090205460ff16612e25576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b601454601254808210612e39575050611b07565b80841115612e45578093505b83821015612fab57600060128381548110612e5c57fe5b90600052602060002001549050306001600160a01b03166327b42c0f82866040518363ffffffff1660e01b815260040180838152602001826001600160a01b0316815260200192505050602060405180830381600087803b158015612ec057600080fd5b505af1925050508015612ee557506040513d6020811015612ee057600080fd5b505160015b612f79576040805163225fc9fd60e01b8152600481018390526001600160a01b03861660248201529051309163225fc9fd9160448083019260209291908290030181600087803b158015612f3857600080fd5b505af1925050508015612f5d57506040513d6020811015612f5857600080fd5b505160015b612f6657612f74565b80612f72575050612fab565b505b612f87565b80612f85575050612fab565b505b60128381548110612f9457fe5b600091825260208220015550600190910190612e45565b506014555050565b60145481565b6002546001600160a01b03163314613006576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b600980546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f828abcccea18192c21d645e575652c49e20b986dab777906fc473d056b01b6a89181900360200190a150565b60105481565b6001546001600160a01b031633146130ad576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001546001600160a01b0316331461311c576040805162461bcd60e51b81526020600482015260156024820152600080516020615da9833981519152604482015290519081900360640190fd5b826001600160a01b031663095ea7b383836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561317357600080fd5b505af1158015613187573d6000803e3d6000fd5b505050506040513d602081101561319d57600080fd5b5050505050565b6002546001600160a01b031633146131f1576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b60005b835181101561328c57600084828151811061320b57fe5b6020026020010151905083828151811061322157fe5b6020026020010151600b6000836001600160a01b03166001600160a01b031681526020019081526020016000208190555082828151811061325e57fe5b6020908102919091018101516001600160a01b039092166000908152600c90915260409020556001016131f4565b507fae32d569b058895b9620d6552b09aaffedc9a6f396be4d595a224ad09f8b213983838360405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156132f95781810151838201526020016132e1565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015613338578181015183820152602001613320565b50505050905001848103825285818151815260200191508051906020019060200280838360005b8381101561337757818101518382015260200161335f565b50505050905001965050505050505060405180910390a1505050565b6002546001600160a01b031633146133e0576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b6001600160a01b0382166000908152600a6020526040902054806134045750611b07565b6001600160a01b0383166000818152600a6020526040812055613428908383613f9e565b604080516001600160a01b0380861682528416602082015280820183905290517f4f1b51dd7a2fcb861aa2670f668be66835c4ee12b4bbbf037e4d0018f39819e49181900360600190a1505050565b6000600260005414156134bf576040805162461bcd60e51b815260206004820152601f6024820152600080516020615d89833981519152604482015290519081900360640190fd5b6002600055600d54841015613501576040805162461bcd60e51b815260206004820152600360248201526266656560e81b604482015290519081900360640190fd5b83341461353b576040805162461bcd60e51b81526020600482015260036024820152621d985b60ea1b604482015290519081900360640190fd5b8a516001148061354c57508a516002145b613583576040805162461bcd60e51b81526020600482015260036024820152623632b760e91b604482015290519081900360640190fd5b61358b61487d565b613594836148e9565b8815613646576005548b516001600160a01b0390911690631b827878908d906000906135bc57fe5b602002602001015133308d6040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b03168152602001828152602001945050505050600060405180830381600087803b15801561362d57600080fd5b505af1158015613641573d6000803e3d6000fd5b505050505b6123cd338c8c8c8c8c8c8c8c60008c61495d565b60145460125460155460135490919293565b3360009081526017602052604090205460ff166136b6576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b6015546013548082106136ca575050611b07565b808411156136d6578093505b8382101561383c576000601383815481106136ed57fe5b90600052602060002001549050306001600160a01b0316630d4d003d82866040518363ffffffff1660e01b815260040180838152602001826001600160a01b0316815260200192505050602060405180830381600087803b15801561375157600080fd5b505af192505050801561377657506040513d602081101561377157600080fd5b505160015b61380a5760408051633051b17160e11b8152600481018390526001600160a01b0386166024820152905130916360a362e29160448083019260209291908290030181600087803b1580156137c957600080fd5b505af19250505080156137ee57506040513d60208110156137e957600080fd5b505160015b6137f757613805565b8061380357505061383c565b505b613818565b8061381657505061383c565b505b6013838154811061382557fe5b6000918252602082200155506001909101906136d6565b506015555050565b6002546001600160a01b031681565b6005546001600160a01b031681565b601a6020526000908152604090205481565b6019602052600090815260409020805460028201546003830154600484015460058501546006860154600787015460088801546009890154600a8a0154600b909a01546001600160a01b03998a169a988a169997989697959660ff958616969495939492939092908216916101009004168c565b6003546001600160a01b031681565b6002546001600160a01b03163314613944576040805162461bcd60e51b815260206004820152601e6024820152600080516020615e84833981519152604482015290519081900360640190fd5b600d8190556040805182815290517f52a8358457e20bbb36e4086b83fb0749599f1893fe4c35a876c46dc4886d12db9181900360200190a150565b60004261399760105485614cce90919063ffffffff16565b116139d3576040805162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b604482015290519081900360640190fd5b6000333014806139f257503360009081526017602052604090205460ff165b60115490915060ff16158015613a06575080155b15613a3e576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b8015613a645743613a5a600e5487614cce90919063ffffffff16565b1115915050613afe565b336001600160a01b03841614613aa7576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b42613abd600f5486614cce90919063ffffffff16565b1115613af8576040805162461bcd60e51b815260206004820152600560248201526464656c617960d81b604482015290519081900360640190fd5b60019150505b9392505050565b6003546000906001600160a01b03168185613b9857816001600160a01b031663e124e6d28a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613b6757600080fd5b505afa158015613b7b573d6000803e3d6000fd5b505050506040513d6020811015613b9157600080fd5b5051613c12565b816001600160a01b03166381a612d68a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613be557600080fd5b505afa158015613bf9573d6000803e3d6000fd5b505050506040513d6020811015613c0f57600080fd5b50515b90508515613c5e5783811015613c595760405162461bcd60e51b8152600401808060200182810382526030815260200180615e546030913960400191505060405180910390fd5b613c9d565b83811115613c9d5760405162461bcd60e51b8152600401808060200182810382526031815260200180615ef46031913960400191505060405180910390fd5b6000826001600160a01b03166312d43a516040518163ffffffff1660e01b815260040160206040518083038186803b158015613cd857600080fd5b505afa158015613cec573d6000803e3d6000fd5b505050506040513d6020811015613d0257600080fd5b81019080805190602001909291905050509050600460009054906101000a90046001600160a01b03166001600160a01b031663f3238cec8d8d8d8b8d8860006040518863ffffffff1660e01b815260040180886001600160a01b03168152602001876001600160a01b03168152602001866001600160a01b0316815260200185151581526020018481526020018381526020018215158152602001975050505050505050600060405180830381600087803b158015613dc057600080fd5b505af1158015613dd4573d6000803e3d6000fd5b50505050806001600160a01b0316636d63c1d0846040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015613e2757600080fd5b505af1158015613e3b573d6000803e3d6000fd5b505050506000600560009054906101000a90046001600160a01b03166001600160a01b0316632662166b8e8e8e8e8e8e8e6040518863ffffffff1660e01b815260040180886001600160a01b03168152602001876001600160a01b03168152602001866001600160a01b031681526020018581526020018481526020018315158152602001826001600160a01b03168152602001975050505050505050602060405180830381600087803b158015613ef257600080fd5b505af1158015613f06573d6000803e3d6000fd5b505050506040513d6020811015613f1c57600080fd5b50516040805163d3c87bbb60e01b81526001600160a01b03878116600483015291519293509084169163d3c87bbb9160248082019260009290919082900301818387803b158015613f6c57600080fd5b505af1158015613f80573d6000803e3d6000fd5b50505050613f8e8d8a614d26565b9c9b505050505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613ff0908490614eaf565b505050565b600083516002141561403c576140358460008151811061401157fe5b60200260200101518560018151811061402657fe5b60200260200101518585614f60565b9050613afe565b60405162461bcd60e51b8152600401808060200182810382526029815260200180615f256029913960400191505060405180910390fd5b60065460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921691632e1a7d4d9160248082019260009290919082900301818387803b1580156140c057600080fd5b505af11580156140d4573d6000803e3d6000fd5b50506040516001600160a01b038416925084156108fc02915084906000818181858888f150505050505050565b6000613afe83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250615036565b6001600160a01b03841661415657614243565b614168846001600160a01b03166150cd565b61417157614243565b6016548061417f5750614243565b6000856001600160a01b031663edf3daec838787876040518563ffffffff1660e01b815260040180848152602001831515815260200182151581526020019350505050600060405180830381600088803b1580156141dc57600080fd5b5087f1935050505080156141ee575060015b6141f7576141fb565b5060015b604080516001600160a01b0388168152821515602082015281517f46ddbd62fc1a7626fe9c43026cb0694aec0b031fe81ac66fb4cfe9381dc6fe72929181900390910190a150505b50505050565b600080333014806139f257503360009081526017602052604090205460ff1660115490915060ff16158015613a06575080613a3e576040805162461bcd60e51b815260206004820152600360248201526234303360e81b604482015290519081900360640190fd5b80471015614306576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015290519081900360640190fd5b6040516000906001600160a01b0384169083908381818185875af1925050503d8060008114614351576040519150601f19603f3d011682016040523d82523d6000602084013e614356565b606091505b5050905080613ff05760405162461bcd60e51b815260040180806020018281038252603a815260200180615df4603a913960400191505060405180910390fd5b6000806143a78888888888886150d3565b905080156144665760006143de6127106143d86143d160075461271061410190919063ffffffff16565b8a906152b0565b90615309565b905060006143ec8883614101565b905060008960018b51038151811061440057fe5b6020026020010151905061444282600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002054614cce90919063ffffffff16565b6001600160a01b039091166000908152600a602052604090205550915061446b9050565b859150505b9695505050505050565b6003546001600160a01b031660008361450657816001600160a01b03166381a612d6876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156144d557600080fd5b505afa1580156144e9573d6000803e3d6000fd5b505050506040513d60208110156144ff57600080fd5b5051614580565b816001600160a01b031663e124e6d2876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561455357600080fd5b505afa158015614567573d6000803e3d6000fd5b505050506040513d602081101561457d57600080fd5b50515b905083156145cc57828111156145c75760405162461bcd60e51b8152600401808060200182810382526031815260200180615ef46031913960400191505060405180910390fd5b61460b565b8281101561460b5760405162461bcd60e51b8152600401808060200182810382526030815260200180615e546030913960400191505060405180910390fd5b614616868587615348565b6000826001600160a01b03166312d43a516040518163ffffffff1660e01b815260040160206040518083038186803b15801561465157600080fd5b505afa158015614665573d6000803e3d6000fd5b505050506040513d602081101561467b57600080fd5b50516004805460408051633cc8e33b60e21b81526001600160a01b038e8116948201949094528c841660248201528b841660448201528915156064820152608481018b905260a48101879052600160c4820152905193945091169163f3238cec9160e48082019260009290919082900301818387803b1580156146fd57600080fd5b505af1158015614711573d6000803e3d6000fd5b50505050806001600160a01b0316636d63c1d0846040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561476457600080fd5b505af1158015614778573d6000803e3d6000fd5b505060055460408051630f8ee8bb60e11b81526001600160a01b038e811660048301528d811660248301528c81166044830152606482018c90528a151560848301529151919092169350631f1dd176925060a480830192600092919082900301818387803b1580156147e957600080fd5b505af11580156147fd573d6000803e3d6000fd5b50505050806001600160a01b031663d3c87bbb846040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561485057600080fd5b505af1158015614864573d6000803e3d6000fd5b5050505061487289876154fa565b505050505050505050565b34156148e757600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156148d357600080fd5b505af115801561319d573d6000803e3d6000fd5b565b801580159061490257506009546001600160a01b031615155b1561495a57600954604080516356b4b2ad60e01b81523360048201526024810184905290516001600160a01b03909216916356b4b2ad9160448082019260009290919082900301818387803b1580156148d357600080fd5b50565b6000614967615c3a565b604051806101a001604052808e6001600160a01b031681526020018d81526020018c6001600160a01b031681526020018b81526020018a815260200189815260200188151581526020018781526020018681526020014381526020014281526020018515158152602001846001600160a01b031681525090506000806149ec83615600565b915091508e6001600160a01b03167f5265bc4952da402633b3fc35f67ab4245493a0ab94dd8ab123667c8d45a4485c8f8f8f8f8f8f8f8f8b60016012805490500343423a60405180806020018e6001600160a01b031681526020018d81526020018c81526020018b81526020018a1515815260200189815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528f818151815260200191508051906020019060200280838360005b83811015614ac3578181015183820152602001614aab565b505050509050019e50505050505050505050505050505060405180910390a29e9d5050505050505050505050505050565b6000614afe615b85565b604051806101c001604052808f6001600160a01b031681526020018e81526020018d6001600160a01b031681526020018c81526020018b81526020018a15158152602001896001600160a01b031681526020018881526020018781526020018681526020014381526020014281526020018515158152602001846001600160a01b03168152509050600080614b928361579e565b9150915082600001516001600160a01b03167f81ed0476a7e785a9e4728fffd679ea97176ca1ac85e1003462558bb5677da57b84602001518560400151866060015187608001518860a001518960c001518a60e001518b61010001518c61012001518c600160138054905003434260405180806020018e6001600160a01b031681526020018d81526020018c81526020018b151581526020018a6001600160a01b0316815260200189815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528f818151815260200191508051906020019060200280838360005b83811015614c9c578181015183820152602001614c84565b505050509050019e50505050505050505050505050505060405180910390a29f9e505050505050505050505050505050565b600082820183811015613afe576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6009546001600160a01b031680614d3d5750611b07565b600080826001600160a01b031663534ef883866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b158015614d8c57600080fd5b505afa158015614da0573d6000803e3d6000fd5b505050506040513d6040811015614db657600080fd5b508051602090910151909250905081614dd157505050611b07565b7f474c763ff84bf2c2039a6d9fea955ecd0f724030e3c365b91169c6a16fe751b78585600360009054906101000a90046001600160a01b03166001600160a01b031663318bc6896040518163ffffffff1660e01b815260040160206040518083038186803b158015614e4257600080fd5b505afa158015614e56573d6000803e3d6000fd5b505050506040513d6020811015614e6c57600080fd5b5051604080516001600160a01b03948516815260208101939093528281019190915260608201869052918416608082015290519081900360a00190a15050505050565b6060614f04826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166159479092919063ffffffff16565b805190915015613ff057808060200190516020811015614f2357600080fd5b5051613ff05760405162461bcd60e51b815260040180806020018281038252602a815260200180615f71602a913960400191505060405180910390fd5b60035460408051634998b10960e11b81526001600160a01b03878116600483015286811660248301528481166044830152915160009384931691639331621291606480830192602092919082900301818787803b158015614fc057600080fd5b505af1158015614fd4573d6000803e3d6000fd5b505050506040513d6020811015614fea57600080fd5b505190508381101561502d5760405162461bcd60e51b815260040180806020018281038252602b815260200180615dc9602b913960400191505060405180910390fd5b95945050505050565b600081848411156150c55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561508a578181015183820152602001615072565b50505050905090810190601f1680156150b75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b3b151590565b6000826150e25750600061446b565b816150ef5750600161446b565b60008660018851038151811061510157fe5b602090810291909101015160035460408051634a3f088d60e01b81526001600160a01b038c81166004830152808516602483015289811660448301528815156064830152915193945091169160009182918491634a3f088d91608480830192610100929190829003018186803b15801561517a57600080fd5b505afa15801561518e573d6000803e3d6000fd5b505050506040513d6101008110156151a557600080fd5b5080516020909101519092509050816151c557600094505050505061446b565b60006151d18388614cce565b90506000846001600160a01b0316630a48d5a9878d6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b15801561522a57600080fd5b505afa15801561523e573d6000803e3d6000fd5b505050506040513d602081101561525457600080fd5b5051905060006152648483614cce565b90506000615278856143d8886127106152b0565b90506000615299836143d860085461271001886152b090919063ffffffff16565b919091109f9e505050505050505050505050505050565b6000826152bf575060006128f5565b828202828482816152cc57fe5b0414613afe5760405162461bcd60e51b8152600401808060200182810382526021815260200180615ed36021913960400191505060405180910390fd5b6000613afe83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b81525061595e565b8061535257613ff0565b8115615445576001600160a01b0383166000908152600b6020526040902054801580159061540357506003546040805163783a2b6760e11b81526001600160a01b0387811660048301529151849361540193879391169163f07456ce91602480820192602092909190829003018186803b1580156153cf57600080fd5b505afa1580156153e3573d6000803e3d6000fd5b505050506040513d60208110156153f957600080fd5b505190614cce565b115b1561543f5760405162461bcd60e51b815260040180806020018281038252602e815260200180615d5b602e913960400191505060405180910390fd5b50613ff0565b6001600160a01b0383166000908152600c602052604090205480158015906154be57506003546040805163114f1b5560e31b81526001600160a01b038781166004830152915184936154bc938793911691638a78daa891602480820192602092909190829003018186803b1580156153cf57600080fd5b115b156142435760405162461bcd60e51b815260040180806020018281038252602f815260200180615ea4602f913960400191505060405180910390fd5b6009546001600160a01b0316806155115750611b07565b600080826001600160a01b031663534ef883866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b15801561556057600080fd5b505afa158015615574573d6000803e3d6000fd5b505050506040513d604081101561558a57600080fd5b5080516020918201516003546040805163318bc68960e01b815290519396509194507fc2414023ce7002ee98557d1e7be21e5559073336f2217ee5f9b2e50fd85f71ee93899389936001600160a01b039093169263318bc689926004808301939192829003018186803b158015614e4257600080fd5b80516001600160a01b03811660009081526018602052604081205490918291829061562c906001614cce565b6001600160a01b038316600090815260186020526040812082905590915061565483836128b5565b6000818152601960209081526040909120885181546001600160a01b0319166001600160a01b03909116178155888201518051939450899391926156a092600185019290910190615cc1565b5060408201516002820180546001600160a01b039283166001600160a01b0319909116179055606083015160038301556080830151600483015560a0830151600583015560c083015160068301805491151560ff1992831617905560e084015160078401556101008085015160088501556101208501516009850155610140850151600a850155610160850151600b90940180546101809096015190931602610100600160a81b0319931515949091169390931791909116919091179055601280546001810182556000919091527fbb8a6a4669ba250d26cd7a459eca9d215f8307e33aebe50379bc5a3617ec344401819055909350915050915091565b80516001600160a01b0381166000908152601a60205260408120549091829182906157ca906001614cce565b6001600160a01b0383166000908152601a602052604081208290559091506157f283836128b5565b6000818152601b60209081526040909120885181546001600160a01b0319166001600160a01b039091161781558882015180519394508993919261583e92600185019290910190615cc1565b5060408201516002820180546001600160a01b039283166001600160a01b0319909116179055606083015160038301556080830151600483015560a083015160058301805460c08601518416610100908102610100600160a81b031994151560ff199384161785161790925560e0860151600686015581860151600786015561012086015160088601556101408601516009860155610160860151600a860155610180860151600b90950180546101a090970151909416909102931515941693909317909216179055601380546001810182556000919091527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a09001819055909350915050915091565b606061595684846000856159c3565b949350505050565b600081836159ad5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561508a578181015183820152602001615072565b5060008385816159b957fe5b0495945050505050565b606082471015615a045760405162461bcd60e51b8152600401808060200182810382526026815260200180615e2e6026913960400191505060405180910390fd5b615a0d856150cd565b615a5e576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310615a9d5780518252601f199092019160209182019101615a7e565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114615aff576040519150601f19603f3d011682016040523d82523d6000602084013e615b04565b606091505b5091509150615b14828286615b1f565b979650505050505050565b60608315615b2e575081613afe565b825115615b3e5782518084602001fd5b60405162461bcd60e51b815260206004820181815284516024840152845185939192839260440191908501908083836000831561508a578181015183820152602001615072565b604051806101c0016040528060006001600160a01b031681526020016060815260200160006001600160a01b03168152602001600081526020016000815260200160001515815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681525090565b508054600082559060005260206000209081019061495a9190615d26565b604051806101a0016040528060006001600160a01b031681526020016060815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000151581526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681525090565b828054828255906000526020600020908101928215615d16579160200282015b82811115615d1657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615ce1565b50615d22929150615d3b565b5090565b5b80821115615d225760008155600101615d27565b5b80821115615d225780546001600160a01b0319168155600101615d3c56fe42617365506f736974696f6e4d616e616765723a206d617820676c6f62616c206c6f6e67732065786365656465645265656e7472616e637947756172643a207265656e7472616e742063616c6c00476f7665726e61626c653a20666f7262696464656e000000000000000000000042617365506f736974696f6e4d616e616765723a20696e73756666696369656e7420616d6f756e744f7574416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d61792068617665207265766572746564416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c42617365506f736974696f6e4d616e616765723a206d61726b207072696365206c6f776572207468616e206c696d697442617365506f736974696f6e4d616e616765723a20666f7262696464656e000042617365506f736974696f6e4d616e616765723a206d617820676c6f62616c2073686f727473206578636565646564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7742617365506f736974696f6e4d616e616765723a206d61726b20707269636520686967686572207468616e206c696d697442617365506f736974696f6e4d616e616765723a20696e76616c6964205f706174682e6c656e67746842617365506f736974696f6e4d616e616765723a20696e76616c69642073656e6465725361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a26469706673582212209ce5a3b6726e45235a68f6f370dd0252abe63f1c6342dc561eba4b4a5871f32764736f6c634300060c0033000000000000000000000000489ee077994b6658eafa855c308275ead8097c4a000000000000000000000000abbc5f99639c9b6bcb58544ddf04efa6802f406400000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000f58eec83ba28ddd79390b9e90c4d3ebff1d434da000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a4000"; + public PositionRouterDeploymentBase() : base(BYTECODE) { } + public PositionRouterDeploymentBase(string byteCode) : base(byteCode) { } + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_router", 2)] + public virtual string Router { get; set; } + [Parameter("address", "_weth", 3)] + public virtual string Weth { get; set; } + [Parameter("address", "_shortsTracker", 4)] + public virtual string ShortsTracker { get; set; } + [Parameter("uint256", "_depositFee", 5)] + public virtual BigInteger DepositFee { get; set; } + [Parameter("uint256", "_minExecutionFee", 6)] + public virtual BigInteger MinExecutionFee { get; set; } + } + + public partial class BasisPointsDivisorFunction : BasisPointsDivisorFunctionBase { } + + [Function("BASIS_POINTS_DIVISOR", "uint256")] + public class BasisPointsDivisorFunctionBase : FunctionMessage + { + + } + + public partial class AdminFunction : AdminFunctionBase { } + + [Function("admin", "address")] + public class AdminFunctionBase : FunctionMessage + { + + } + + public partial class ApproveFunction : ApproveFunctionBase { } + + [Function("approve")] + public class ApproveFunctionBase : FunctionMessage + { + [Parameter("address", "_token", 1)] + public virtual string Token { get; set; } + [Parameter("address", "_spender", 2)] + public virtual string Spender { get; set; } + [Parameter("uint256", "_amount", 3)] + public virtual BigInteger Amount { get; set; } + } + + public partial class CallbackGasLimitFunction : CallbackGasLimitFunctionBase { } + + [Function("callbackGasLimit", "uint256")] + public class CallbackGasLimitFunctionBase : FunctionMessage + { + + } + + public partial class CancelDecreasePositionFunction : CancelDecreasePositionFunctionBase { } + + [Function("cancelDecreasePosition", "bool")] + public class CancelDecreasePositionFunctionBase : FunctionMessage + { + [Parameter("bytes32", "_key", 1)] + public virtual byte[] Key { get; set; } + [Parameter("address", "_executionFeeReceiver", 2)] + public virtual string ExecutionFeeReceiver { get; set; } + } + + public partial class CancelIncreasePositionFunction : CancelIncreasePositionFunctionBase { } + + [Function("cancelIncreasePosition", "bool")] + public class CancelIncreasePositionFunctionBase : FunctionMessage + { + [Parameter("bytes32", "_key", 1)] + public virtual byte[] Key { get; set; } + [Parameter("address", "_executionFeeReceiver", 2)] + public virtual string ExecutionFeeReceiver { get; set; } + } + + public partial class CreateDecreasePositionFunction : CreateDecreasePositionFunctionBase { } + + [Function("createDecreasePosition", "bytes32")] + public class CreateDecreasePositionFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_collateralDelta", 3)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("address", "_receiver", 6)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "_acceptablePrice", 7)] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "_minOut", 8)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_executionFee", 9)] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("bool", "_withdrawETH", 10)] + public virtual bool WithdrawETH { get; set; } + [Parameter("address", "_callbackTarget", 11)] + public virtual string CallbackTarget { get; set; } + } + + public partial class CreateIncreasePositionFunction : CreateIncreasePositionFunctionBase { } + + [Function("createIncreasePosition", "bytes32")] + public class CreateIncreasePositionFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_amountIn", 3)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "_minOut", 4)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_sizeDelta", 5)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 6)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "_acceptablePrice", 7)] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "_executionFee", 8)] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("bytes32", "_referralCode", 9)] + public virtual byte[] ReferralCode { get; set; } + [Parameter("address", "_callbackTarget", 10)] + public virtual string CallbackTarget { get; set; } + } + + public partial class CreateIncreasePositionETHFunction : CreateIncreasePositionETHFunctionBase { } + + [Function("createIncreasePositionETH", "bytes32")] + public class CreateIncreasePositionETHFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_minOut", 3)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "_acceptablePrice", 6)] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "_executionFee", 7)] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("bytes32", "_referralCode", 8)] + public virtual byte[] ReferralCode { get; set; } + [Parameter("address", "_callbackTarget", 9)] + public virtual string CallbackTarget { get; set; } + } + + public partial class DecreasePositionRequestKeysFunction : DecreasePositionRequestKeysFunctionBase { } + + [Function("decreasePositionRequestKeys", "bytes32")] + public class DecreasePositionRequestKeysFunctionBase : FunctionMessage + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class DecreasePositionRequestKeysStartFunction : DecreasePositionRequestKeysStartFunctionBase { } + + [Function("decreasePositionRequestKeysStart", "uint256")] + public class DecreasePositionRequestKeysStartFunctionBase : FunctionMessage + { + + } + + public partial class DecreasePositionRequestsFunction : DecreasePositionRequestsFunctionBase { } + + [Function("decreasePositionRequests", typeof(DecreasePositionRequestsOutputDTO))] + public class DecreasePositionRequestsFunctionBase : FunctionMessage + { + [Parameter("bytes32", "", 1)] + public virtual byte[] ReturnValue1 { get; set; } + } + + public partial class DecreasePositionsIndexFunction : DecreasePositionsIndexFunctionBase { } + + [Function("decreasePositionsIndex", "uint256")] + public class DecreasePositionsIndexFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class DepositFeeFunction : DepositFeeFunctionBase { } + + [Function("depositFee", "uint256")] + public class DepositFeeFunctionBase : FunctionMessage + { + + } + + public partial class ExecuteDecreasePositionFunction : ExecuteDecreasePositionFunctionBase { } + + [Function("executeDecreasePosition", "bool")] + public class ExecuteDecreasePositionFunctionBase : FunctionMessage + { + [Parameter("bytes32", "_key", 1)] + public virtual byte[] Key { get; set; } + [Parameter("address", "_executionFeeReceiver", 2)] + public virtual string ExecutionFeeReceiver { get; set; } + } + + public partial class ExecuteDecreasePositionsFunction : ExecuteDecreasePositionsFunctionBase { } + + [Function("executeDecreasePositions")] + public class ExecuteDecreasePositionsFunctionBase : FunctionMessage + { + [Parameter("uint256", "_endIndex", 1)] + public virtual BigInteger EndIndex { get; set; } + [Parameter("address", "_executionFeeReceiver", 2)] + public virtual string ExecutionFeeReceiver { get; set; } + } + + public partial class ExecuteIncreasePositionFunction : ExecuteIncreasePositionFunctionBase { } + + [Function("executeIncreasePosition", "bool")] + public class ExecuteIncreasePositionFunctionBase : FunctionMessage + { + [Parameter("bytes32", "_key", 1)] + public virtual byte[] Key { get; set; } + [Parameter("address", "_executionFeeReceiver", 2)] + public virtual string ExecutionFeeReceiver { get; set; } + } + + public partial class ExecuteIncreasePositionsFunction : ExecuteIncreasePositionsFunctionBase { } + + [Function("executeIncreasePositions")] + public class ExecuteIncreasePositionsFunctionBase : FunctionMessage + { + [Parameter("uint256", "_endIndex", 1)] + public virtual BigInteger EndIndex { get; set; } + [Parameter("address", "_executionFeeReceiver", 2)] + public virtual string ExecutionFeeReceiver { get; set; } + } + + public partial class FeeReservesFunction : FeeReservesFunctionBase { } + + [Function("feeReserves", "uint256")] + public class FeeReservesFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class GetDecreasePositionRequestPathFunction : GetDecreasePositionRequestPathFunctionBase { } + + [Function("getDecreasePositionRequestPath", "address[]")] + public class GetDecreasePositionRequestPathFunctionBase : FunctionMessage + { + [Parameter("bytes32", "_key", 1)] + public virtual byte[] Key { get; set; } + } + + public partial class GetIncreasePositionRequestPathFunction : GetIncreasePositionRequestPathFunctionBase { } + + [Function("getIncreasePositionRequestPath", "address[]")] + public class GetIncreasePositionRequestPathFunctionBase : FunctionMessage + { + [Parameter("bytes32", "_key", 1)] + public virtual byte[] Key { get; set; } + } + + public partial class GetRequestKeyFunction : GetRequestKeyFunctionBase { } + + [Function("getRequestKey", "bytes32")] + public class GetRequestKeyFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("uint256", "_index", 2)] + public virtual BigInteger Index { get; set; } + } + + public partial class GetRequestQueueLengthsFunction : GetRequestQueueLengthsFunctionBase { } + + [Function("getRequestQueueLengths", typeof(GetRequestQueueLengthsOutputDTO))] + public class GetRequestQueueLengthsFunctionBase : FunctionMessage + { + + } + + public partial class GovFunction : GovFunctionBase { } + + [Function("gov", "address")] + public class GovFunctionBase : FunctionMessage + { + + } + + public partial class IncreasePositionBufferBpsFunction : IncreasePositionBufferBpsFunctionBase { } + + [Function("increasePositionBufferBps", "uint256")] + public class IncreasePositionBufferBpsFunctionBase : FunctionMessage + { + + } + + public partial class IncreasePositionRequestKeysFunction : IncreasePositionRequestKeysFunctionBase { } + + [Function("increasePositionRequestKeys", "bytes32")] + public class IncreasePositionRequestKeysFunctionBase : FunctionMessage + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class IncreasePositionRequestKeysStartFunction : IncreasePositionRequestKeysStartFunctionBase { } + + [Function("increasePositionRequestKeysStart", "uint256")] + public class IncreasePositionRequestKeysStartFunctionBase : FunctionMessage + { + + } + + public partial class IncreasePositionRequestsFunction : IncreasePositionRequestsFunctionBase { } + + [Function("increasePositionRequests", typeof(IncreasePositionRequestsOutputDTO))] + public class IncreasePositionRequestsFunctionBase : FunctionMessage + { + [Parameter("bytes32", "", 1)] + public virtual byte[] ReturnValue1 { get; set; } + } + + public partial class IncreasePositionsIndexFunction : IncreasePositionsIndexFunctionBase { } + + [Function("increasePositionsIndex", "uint256")] + public class IncreasePositionsIndexFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class IsLeverageEnabledFunction : IsLeverageEnabledFunctionBase { } + + [Function("isLeverageEnabled", "bool")] + public class IsLeverageEnabledFunctionBase : FunctionMessage + { + + } + + public partial class IsPositionKeeperFunction : IsPositionKeeperFunctionBase { } + + [Function("isPositionKeeper", "bool")] + public class IsPositionKeeperFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class MaxGlobalLongSizesFunction : MaxGlobalLongSizesFunctionBase { } + + [Function("maxGlobalLongSizes", "uint256")] + public class MaxGlobalLongSizesFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class MaxGlobalShortSizesFunction : MaxGlobalShortSizesFunctionBase { } + + [Function("maxGlobalShortSizes", "uint256")] + public class MaxGlobalShortSizesFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class MaxTimeDelayFunction : MaxTimeDelayFunctionBase { } + + [Function("maxTimeDelay", "uint256")] + public class MaxTimeDelayFunctionBase : FunctionMessage + { + + } + + public partial class MinBlockDelayKeeperFunction : MinBlockDelayKeeperFunctionBase { } + + [Function("minBlockDelayKeeper", "uint256")] + public class MinBlockDelayKeeperFunctionBase : FunctionMessage + { + + } + + public partial class MinExecutionFeeFunction : MinExecutionFeeFunctionBase { } + + [Function("minExecutionFee", "uint256")] + public class MinExecutionFeeFunctionBase : FunctionMessage + { + + } + + public partial class MinTimeDelayPublicFunction : MinTimeDelayPublicFunctionBase { } + + [Function("minTimeDelayPublic", "uint256")] + public class MinTimeDelayPublicFunctionBase : FunctionMessage + { + + } + + public partial class ReferralStorageFunction : ReferralStorageFunctionBase { } + + [Function("referralStorage", "address")] + public class ReferralStorageFunctionBase : FunctionMessage + { + + } + + public partial class RouterFunction : RouterFunctionBase { } + + [Function("router", "address")] + public class RouterFunctionBase : FunctionMessage + { + + } + + public partial class SendValueFunction : SendValueFunctionBase { } + + [Function("sendValue")] + public class SendValueFunctionBase : FunctionMessage + { + [Parameter("address", "_receiver", 1)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "_amount", 2)] + public virtual BigInteger Amount { get; set; } + } + + public partial class SetAdminFunction : SetAdminFunctionBase { } + + [Function("setAdmin")] + public class SetAdminFunctionBase : FunctionMessage + { + [Parameter("address", "_admin", 1)] + public virtual string Admin { get; set; } + } + + public partial class SetCallbackGasLimitFunction : SetCallbackGasLimitFunctionBase { } + + [Function("setCallbackGasLimit")] + public class SetCallbackGasLimitFunctionBase : FunctionMessage + { + [Parameter("uint256", "_callbackGasLimit", 1)] + public virtual BigInteger CallbackGasLimit { get; set; } + } + + public partial class SetDelayValuesFunction : SetDelayValuesFunctionBase { } + + [Function("setDelayValues")] + public class SetDelayValuesFunctionBase : FunctionMessage + { + [Parameter("uint256", "_minBlockDelayKeeper", 1)] + public virtual BigInteger MinBlockDelayKeeper { get; set; } + [Parameter("uint256", "_minTimeDelayPublic", 2)] + public virtual BigInteger MinTimeDelayPublic { get; set; } + [Parameter("uint256", "_maxTimeDelay", 3)] + public virtual BigInteger MaxTimeDelay { get; set; } + } + + public partial class SetDepositFeeFunction : SetDepositFeeFunctionBase { } + + [Function("setDepositFee")] + public class SetDepositFeeFunctionBase : FunctionMessage + { + [Parameter("uint256", "_depositFee", 1)] + public virtual BigInteger DepositFee { get; set; } + } + + public partial class SetGovFunction : SetGovFunctionBase { } + + [Function("setGov")] + public class SetGovFunctionBase : FunctionMessage + { + [Parameter("address", "_gov", 1)] + public virtual string Gov { get; set; } + } + + public partial class SetIncreasePositionBufferBpsFunction : SetIncreasePositionBufferBpsFunctionBase { } + + [Function("setIncreasePositionBufferBps")] + public class SetIncreasePositionBufferBpsFunctionBase : FunctionMessage + { + [Parameter("uint256", "_increasePositionBufferBps", 1)] + public virtual BigInteger IncreasePositionBufferBps { get; set; } + } + + public partial class SetIsLeverageEnabledFunction : SetIsLeverageEnabledFunctionBase { } + + [Function("setIsLeverageEnabled")] + public class SetIsLeverageEnabledFunctionBase : FunctionMessage + { + [Parameter("bool", "_isLeverageEnabled", 1)] + public virtual bool IsLeverageEnabled { get; set; } + } + + public partial class SetMaxGlobalSizesFunction : SetMaxGlobalSizesFunctionBase { } + + [Function("setMaxGlobalSizes")] + public class SetMaxGlobalSizesFunctionBase : FunctionMessage + { + [Parameter("address[]", "_tokens", 1)] + public virtual List Tokens { get; set; } + [Parameter("uint256[]", "_longSizes", 2)] + public virtual List LongSizes { get; set; } + [Parameter("uint256[]", "_shortSizes", 3)] + public virtual List ShortSizes { get; set; } + } + + public partial class SetMinExecutionFeeFunction : SetMinExecutionFeeFunctionBase { } + + [Function("setMinExecutionFee")] + public class SetMinExecutionFeeFunctionBase : FunctionMessage + { + [Parameter("uint256", "_minExecutionFee", 1)] + public virtual BigInteger MinExecutionFee { get; set; } + } + + public partial class SetPositionKeeperFunction : SetPositionKeeperFunctionBase { } + + [Function("setPositionKeeper")] + public class SetPositionKeeperFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("bool", "_isActive", 2)] + public virtual bool IsActive { get; set; } + } + + public partial class SetReferralStorageFunction : SetReferralStorageFunctionBase { } + + [Function("setReferralStorage")] + public class SetReferralStorageFunctionBase : FunctionMessage + { + [Parameter("address", "_referralStorage", 1)] + public virtual string ReferralStorage { get; set; } + } + + public partial class SetRequestKeysStartValuesFunction : SetRequestKeysStartValuesFunctionBase { } + + [Function("setRequestKeysStartValues")] + public class SetRequestKeysStartValuesFunctionBase : FunctionMessage + { + [Parameter("uint256", "_increasePositionRequestKeysStart", 1)] + public virtual BigInteger IncreasePositionRequestKeysStart { get; set; } + [Parameter("uint256", "_decreasePositionRequestKeysStart", 2)] + public virtual BigInteger DecreasePositionRequestKeysStart { get; set; } + } + + public partial class ShortsTrackerFunction : ShortsTrackerFunctionBase { } + + [Function("shortsTracker", "address")] + public class ShortsTrackerFunctionBase : FunctionMessage + { + + } + + public partial class VaultFunction : VaultFunctionBase { } + + [Function("vault", "address")] + public class VaultFunctionBase : FunctionMessage + { + + } + + public partial class WethFunction : WethFunctionBase { } + + [Function("weth", "address")] + public class WethFunctionBase : FunctionMessage + { + + } + + public partial class WithdrawFeesFunction : WithdrawFeesFunctionBase { } + + [Function("withdrawFees")] + public class WithdrawFeesFunctionBase : FunctionMessage + { + [Parameter("address", "_token", 1)] + public virtual string Token { get; set; } + [Parameter("address", "_receiver", 2)] + public virtual string Receiver { get; set; } + } + + public partial class CallbackEventDTO : CallbackEventDTOBase { } + + [Event("Callback")] + public class CallbackEventDTOBase : IEventDTO + { + [Parameter("address", "callbackTarget", 1, false )] + public virtual string CallbackTarget { get; set; } + [Parameter("bool", "success", 2, false )] + public virtual bool Success { get; set; } + } + + public partial class CancelDecreasePositionEventDTO : CancelDecreasePositionEventDTOBase { } + + [Event("CancelDecreasePosition")] + public class CancelDecreasePositionEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("address[]", "path", 2, false )] + public virtual List Path { get; set; } + [Parameter("address", "indexToken", 3, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "collateralDelta", 4, false )] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "sizeDelta", 5, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 6, false )] + public virtual bool IsLong { get; set; } + [Parameter("address", "receiver", 7, false )] + public virtual string Receiver { get; set; } + [Parameter("uint256", "acceptablePrice", 8, false )] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "minOut", 9, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "executionFee", 10, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "blockGap", 11, false )] + public virtual BigInteger BlockGap { get; set; } + [Parameter("uint256", "timeGap", 12, false )] + public virtual BigInteger TimeGap { get; set; } + } + + public partial class CancelIncreasePositionEventDTO : CancelIncreasePositionEventDTOBase { } + + [Event("CancelIncreasePosition")] + public class CancelIncreasePositionEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("address[]", "path", 2, false )] + public virtual List Path { get; set; } + [Parameter("address", "indexToken", 3, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "acceptablePrice", 8, false )] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "executionFee", 9, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "blockGap", 10, false )] + public virtual BigInteger BlockGap { get; set; } + [Parameter("uint256", "timeGap", 11, false )] + public virtual BigInteger TimeGap { get; set; } + } + + public partial class CreateDecreasePositionEventDTO : CreateDecreasePositionEventDTOBase { } + + [Event("CreateDecreasePosition")] + public class CreateDecreasePositionEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("address[]", "path", 2, false )] + public virtual List Path { get; set; } + [Parameter("address", "indexToken", 3, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "collateralDelta", 4, false )] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "sizeDelta", 5, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 6, false )] + public virtual bool IsLong { get; set; } + [Parameter("address", "receiver", 7, false )] + public virtual string Receiver { get; set; } + [Parameter("uint256", "acceptablePrice", 8, false )] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "minOut", 9, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "executionFee", 10, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "index", 11, false )] + public virtual BigInteger Index { get; set; } + [Parameter("uint256", "queueIndex", 12, false )] + public virtual BigInteger QueueIndex { get; set; } + [Parameter("uint256", "blockNumber", 13, false )] + public virtual BigInteger BlockNumber { get; set; } + [Parameter("uint256", "blockTime", 14, false )] + public virtual BigInteger BlockTime { get; set; } + } + + public partial class CreateIncreasePositionEventDTO : CreateIncreasePositionEventDTOBase { } + + [Event("CreateIncreasePosition")] + public class CreateIncreasePositionEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("address[]", "path", 2, false )] + public virtual List Path { get; set; } + [Parameter("address", "indexToken", 3, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "acceptablePrice", 8, false )] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "executionFee", 9, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "index", 10, false )] + public virtual BigInteger Index { get; set; } + [Parameter("uint256", "queueIndex", 11, false )] + public virtual BigInteger QueueIndex { get; set; } + [Parameter("uint256", "blockNumber", 12, false )] + public virtual BigInteger BlockNumber { get; set; } + [Parameter("uint256", "blockTime", 13, false )] + public virtual BigInteger BlockTime { get; set; } + [Parameter("uint256", "gasPrice", 14, false )] + public virtual BigInteger GasPrice { get; set; } + } + + public partial class DecreasePositionReferralEventDTO : DecreasePositionReferralEventDTOBase { } + + [Event("DecreasePositionReferral")] + public class DecreasePositionReferralEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, false )] + public virtual string Account { get; set; } + [Parameter("uint256", "sizeDelta", 2, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("uint256", "marginFeeBasisPoints", 3, false )] + public virtual BigInteger MarginFeeBasisPoints { get; set; } + [Parameter("bytes32", "referralCode", 4, false )] + public virtual byte[] ReferralCode { get; set; } + [Parameter("address", "referrer", 5, false )] + public virtual string Referrer { get; set; } + } + + public partial class ExecuteDecreasePositionEventDTO : ExecuteDecreasePositionEventDTOBase { } + + [Event("ExecuteDecreasePosition")] + public class ExecuteDecreasePositionEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("address[]", "path", 2, false )] + public virtual List Path { get; set; } + [Parameter("address", "indexToken", 3, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "collateralDelta", 4, false )] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "sizeDelta", 5, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 6, false )] + public virtual bool IsLong { get; set; } + [Parameter("address", "receiver", 7, false )] + public virtual string Receiver { get; set; } + [Parameter("uint256", "acceptablePrice", 8, false )] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "minOut", 9, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "executionFee", 10, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "blockGap", 11, false )] + public virtual BigInteger BlockGap { get; set; } + [Parameter("uint256", "timeGap", 12, false )] + public virtual BigInteger TimeGap { get; set; } + } + + public partial class ExecuteIncreasePositionEventDTO : ExecuteIncreasePositionEventDTOBase { } + + [Event("ExecuteIncreasePosition")] + public class ExecuteIncreasePositionEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("address[]", "path", 2, false )] + public virtual List Path { get; set; } + [Parameter("address", "indexToken", 3, false )] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 5, false )] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "sizeDelta", 6, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 7, false )] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "acceptablePrice", 8, false )] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "executionFee", 9, false )] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "blockGap", 10, false )] + public virtual BigInteger BlockGap { get; set; } + [Parameter("uint256", "timeGap", 11, false )] + public virtual BigInteger TimeGap { get; set; } + } + + public partial class IncreasePositionReferralEventDTO : IncreasePositionReferralEventDTOBase { } + + [Event("IncreasePositionReferral")] + public class IncreasePositionReferralEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, false )] + public virtual string Account { get; set; } + [Parameter("uint256", "sizeDelta", 2, false )] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("uint256", "marginFeeBasisPoints", 3, false )] + public virtual BigInteger MarginFeeBasisPoints { get; set; } + [Parameter("bytes32", "referralCode", 4, false )] + public virtual byte[] ReferralCode { get; set; } + [Parameter("address", "referrer", 5, false )] + public virtual string Referrer { get; set; } + } + + public partial class SetAdminEventDTO : SetAdminEventDTOBase { } + + [Event("SetAdmin")] + public class SetAdminEventDTOBase : IEventDTO + { + [Parameter("address", "admin", 1, false )] + public virtual string Admin { get; set; } + } + + public partial class SetCallbackGasLimitEventDTO : SetCallbackGasLimitEventDTOBase { } + + [Event("SetCallbackGasLimit")] + public class SetCallbackGasLimitEventDTOBase : IEventDTO + { + [Parameter("uint256", "callbackGasLimit", 1, false )] + public virtual BigInteger CallbackGasLimit { get; set; } + } + + public partial class SetDelayValuesEventDTO : SetDelayValuesEventDTOBase { } + + [Event("SetDelayValues")] + public class SetDelayValuesEventDTOBase : IEventDTO + { + [Parameter("uint256", "minBlockDelayKeeper", 1, false )] + public virtual BigInteger MinBlockDelayKeeper { get; set; } + [Parameter("uint256", "minTimeDelayPublic", 2, false )] + public virtual BigInteger MinTimeDelayPublic { get; set; } + [Parameter("uint256", "maxTimeDelay", 3, false )] + public virtual BigInteger MaxTimeDelay { get; set; } + } + + public partial class SetDepositFeeEventDTO : SetDepositFeeEventDTOBase { } + + [Event("SetDepositFee")] + public class SetDepositFeeEventDTOBase : IEventDTO + { + [Parameter("uint256", "depositFee", 1, false )] + public virtual BigInteger DepositFee { get; set; } + } + + public partial class SetIncreasePositionBufferBpsEventDTO : SetIncreasePositionBufferBpsEventDTOBase { } + + [Event("SetIncreasePositionBufferBps")] + public class SetIncreasePositionBufferBpsEventDTOBase : IEventDTO + { + [Parameter("uint256", "increasePositionBufferBps", 1, false )] + public virtual BigInteger IncreasePositionBufferBps { get; set; } + } + + public partial class SetIsLeverageEnabledEventDTO : SetIsLeverageEnabledEventDTOBase { } + + [Event("SetIsLeverageEnabled")] + public class SetIsLeverageEnabledEventDTOBase : IEventDTO + { + [Parameter("bool", "isLeverageEnabled", 1, false )] + public virtual bool IsLeverageEnabled { get; set; } + } + + public partial class SetMaxGlobalSizesEventDTO : SetMaxGlobalSizesEventDTOBase { } + + [Event("SetMaxGlobalSizes")] + public class SetMaxGlobalSizesEventDTOBase : IEventDTO + { + [Parameter("address[]", "tokens", 1, false )] + public virtual List Tokens { get; set; } + [Parameter("uint256[]", "longSizes", 2, false )] + public virtual List LongSizes { get; set; } + [Parameter("uint256[]", "shortSizes", 3, false )] + public virtual List ShortSizes { get; set; } + } + + public partial class SetMinExecutionFeeEventDTO : SetMinExecutionFeeEventDTOBase { } + + [Event("SetMinExecutionFee")] + public class SetMinExecutionFeeEventDTOBase : IEventDTO + { + [Parameter("uint256", "minExecutionFee", 1, false )] + public virtual BigInteger MinExecutionFee { get; set; } + } + + public partial class SetPositionKeeperEventDTO : SetPositionKeeperEventDTOBase { } + + [Event("SetPositionKeeper")] + public class SetPositionKeeperEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, true )] + public virtual string Account { get; set; } + [Parameter("bool", "isActive", 2, false )] + public virtual bool IsActive { get; set; } + } + + public partial class SetReferralStorageEventDTO : SetReferralStorageEventDTOBase { } + + [Event("SetReferralStorage")] + public class SetReferralStorageEventDTOBase : IEventDTO + { + [Parameter("address", "referralStorage", 1, false )] + public virtual string ReferralStorage { get; set; } + } + + public partial class SetRequestKeysStartValuesEventDTO : SetRequestKeysStartValuesEventDTOBase { } + + [Event("SetRequestKeysStartValues")] + public class SetRequestKeysStartValuesEventDTOBase : IEventDTO + { + [Parameter("uint256", "increasePositionRequestKeysStart", 1, false )] + public virtual BigInteger IncreasePositionRequestKeysStart { get; set; } + [Parameter("uint256", "decreasePositionRequestKeysStart", 2, false )] + public virtual BigInteger DecreasePositionRequestKeysStart { get; set; } + } + + public partial class WithdrawFeesEventDTO : WithdrawFeesEventDTOBase { } + + [Event("WithdrawFees")] + public class WithdrawFeesEventDTOBase : IEventDTO + { + [Parameter("address", "token", 1, false )] + public virtual string Token { get; set; } + [Parameter("address", "receiver", 2, false )] + public virtual string Receiver { get; set; } + [Parameter("uint256", "amount", 3, false )] + public virtual BigInteger Amount { get; set; } + } + + public partial class BasisPointsDivisorOutputDTO : BasisPointsDivisorOutputDTOBase { } + + [FunctionOutput] + public class BasisPointsDivisorOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class AdminOutputDTO : AdminOutputDTOBase { } + + [FunctionOutput] + public class AdminOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + + + public partial class CallbackGasLimitOutputDTO : CallbackGasLimitOutputDTOBase { } + + [FunctionOutput] + public class CallbackGasLimitOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + + + + + + + + + + + public partial class DecreasePositionRequestKeysOutputDTO : DecreasePositionRequestKeysOutputDTOBase { } + + [FunctionOutput] + public class DecreasePositionRequestKeysOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bytes32", "", 1)] + public virtual byte[] ReturnValue1 { get; set; } + } + + public partial class DecreasePositionRequestKeysStartOutputDTO : DecreasePositionRequestKeysStartOutputDTOBase { } + + [FunctionOutput] + public class DecreasePositionRequestKeysStartOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class DecreasePositionRequestsOutputDTO : DecreasePositionRequestsOutputDTOBase { } + + [FunctionOutput] + public class DecreasePositionRequestsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "account", 1)] + public virtual string Account { get; set; } + [Parameter("address", "indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "collateralDelta", 3)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("address", "receiver", 6)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "acceptablePrice", 7)] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "minOut", 8)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "executionFee", 9)] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "blockNumber", 10)] + public virtual BigInteger BlockNumber { get; set; } + [Parameter("uint256", "blockTime", 11)] + public virtual BigInteger BlockTime { get; set; } + [Parameter("bool", "withdrawETH", 12)] + public virtual bool WithdrawETH { get; set; } + [Parameter("address", "callbackTarget", 13)] + public virtual string CallbackTarget { get; set; } + } + + public partial class DecreasePositionsIndexOutputDTO : DecreasePositionsIndexOutputDTOBase { } + + [FunctionOutput] + public class DecreasePositionsIndexOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class DepositFeeOutputDTO : DepositFeeOutputDTOBase { } + + [FunctionOutput] + public class DepositFeeOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + + + + + + + + + public partial class FeeReservesOutputDTO : FeeReservesOutputDTOBase { } + + [FunctionOutput] + public class FeeReservesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class GetDecreasePositionRequestPathOutputDTO : GetDecreasePositionRequestPathOutputDTOBase { } + + [FunctionOutput] + public class GetDecreasePositionRequestPathOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetIncreasePositionRequestPathOutputDTO : GetIncreasePositionRequestPathOutputDTOBase { } + + [FunctionOutput] + public class GetIncreasePositionRequestPathOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetRequestKeyOutputDTO : GetRequestKeyOutputDTOBase { } + + [FunctionOutput] + public class GetRequestKeyOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bytes32", "", 1)] + public virtual byte[] ReturnValue1 { get; set; } + } + + public partial class GetRequestQueueLengthsOutputDTO : GetRequestQueueLengthsOutputDTOBase { } + + [FunctionOutput] + public class GetRequestQueueLengthsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + [Parameter("uint256", "", 2)] + public virtual BigInteger ReturnValue2 { get; set; } + [Parameter("uint256", "", 3)] + public virtual BigInteger ReturnValue3 { get; set; } + [Parameter("uint256", "", 4)] + public virtual BigInteger ReturnValue4 { get; set; } + } + + public partial class GovOutputDTO : GovOutputDTOBase { } + + [FunctionOutput] + public class GovOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class IncreasePositionBufferBpsOutputDTO : IncreasePositionBufferBpsOutputDTOBase { } + + [FunctionOutput] + public class IncreasePositionBufferBpsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class IncreasePositionRequestKeysOutputDTO : IncreasePositionRequestKeysOutputDTOBase { } + + [FunctionOutput] + public class IncreasePositionRequestKeysOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bytes32", "", 1)] + public virtual byte[] ReturnValue1 { get; set; } + } + + public partial class IncreasePositionRequestKeysStartOutputDTO : IncreasePositionRequestKeysStartOutputDTOBase { } + + [FunctionOutput] + public class IncreasePositionRequestKeysStartOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class IncreasePositionRequestsOutputDTO : IncreasePositionRequestsOutputDTOBase { } + + [FunctionOutput] + public class IncreasePositionRequestsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "account", 1)] + public virtual string Account { get; set; } + [Parameter("address", "indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "amountIn", 3)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "minOut", 4)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "sizeDelta", 5)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "isLong", 6)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "acceptablePrice", 7)] + public virtual BigInteger AcceptablePrice { get; set; } + [Parameter("uint256", "executionFee", 8)] + public virtual BigInteger ExecutionFee { get; set; } + [Parameter("uint256", "blockNumber", 9)] + public virtual BigInteger BlockNumber { get; set; } + [Parameter("uint256", "blockTime", 10)] + public virtual BigInteger BlockTime { get; set; } + [Parameter("bool", "hasCollateralInETH", 11)] + public virtual bool HasCollateralInETH { get; set; } + [Parameter("address", "callbackTarget", 12)] + public virtual string CallbackTarget { get; set; } + } + + public partial class IncreasePositionsIndexOutputDTO : IncreasePositionsIndexOutputDTOBase { } + + [FunctionOutput] + public class IncreasePositionsIndexOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class IsLeverageEnabledOutputDTO : IsLeverageEnabledOutputDTOBase { } + + [FunctionOutput] + public class IsLeverageEnabledOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bool", "", 1)] + public virtual bool ReturnValue1 { get; set; } + } + + public partial class IsPositionKeeperOutputDTO : IsPositionKeeperOutputDTOBase { } + + [FunctionOutput] + public class IsPositionKeeperOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bool", "", 1)] + public virtual bool ReturnValue1 { get; set; } + } + + public partial class MaxGlobalLongSizesOutputDTO : MaxGlobalLongSizesOutputDTOBase { } + + [FunctionOutput] + public class MaxGlobalLongSizesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class MaxGlobalShortSizesOutputDTO : MaxGlobalShortSizesOutputDTOBase { } + + [FunctionOutput] + public class MaxGlobalShortSizesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class MaxTimeDelayOutputDTO : MaxTimeDelayOutputDTOBase { } + + [FunctionOutput] + public class MaxTimeDelayOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class MinBlockDelayKeeperOutputDTO : MinBlockDelayKeeperOutputDTOBase { } + + [FunctionOutput] + public class MinBlockDelayKeeperOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class MinExecutionFeeOutputDTO : MinExecutionFeeOutputDTOBase { } + + [FunctionOutput] + public class MinExecutionFeeOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class MinTimeDelayPublicOutputDTO : MinTimeDelayPublicOutputDTOBase { } + + [FunctionOutput] + public class MinTimeDelayPublicOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class ReferralStorageOutputDTO : ReferralStorageOutputDTOBase { } + + [FunctionOutput] + public class ReferralStorageOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class RouterOutputDTO : RouterOutputDTOBase { } + + [FunctionOutput] + public class RouterOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + public partial class ShortsTrackerOutputDTO : ShortsTrackerOutputDTOBase { } + + [FunctionOutput] + public class ShortsTrackerOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class VaultOutputDTO : VaultOutputDTOBase { } + + [FunctionOutput] + public class VaultOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class WethOutputDTO : WethOutputDTOBase { } + + [FunctionOutput] + public class WethOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + +} diff --git a/src/Managing.Tools.ABI/PositionRouter/PositionRouterService.cs b/src/Managing.Tools.ABI/PositionRouter/PositionRouterService.cs new file mode 100644 index 0000000..3655814 --- /dev/null +++ b/src/Managing.Tools.ABI/PositionRouter/PositionRouterService.cs @@ -0,0 +1,1142 @@ +using System.Numerics; +using Nethereum.Web3; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.Contracts.ContractHandlers; +using Managing.Tools.PositionRouter.ContractDefinition; + +namespace Managing.Tools.PositionRouter +{ + public partial class PositionRouterService + { + public static Task DeployContractAndWaitForReceiptAsync(Web3 web3, PositionRouterDeployment positionRouterDeployment, CancellationTokenSource cancellationTokenSource = null) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAndWaitForReceiptAsync(positionRouterDeployment, cancellationTokenSource); + } + + public static Task DeployContractAsync(Web3 web3, PositionRouterDeployment positionRouterDeployment) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAsync(positionRouterDeployment); + } + + public static async Task DeployContractAndGetServiceAsync(Web3 web3, PositionRouterDeployment positionRouterDeployment, CancellationTokenSource cancellationTokenSource = null) + { + var receipt = await DeployContractAndWaitForReceiptAsync(web3, positionRouterDeployment, cancellationTokenSource); + return new PositionRouterService(web3, receipt.ContractAddress); + } + + protected IWeb3 Web3 { get; } + + public ContractHandler ContractHandler { get; } + + public PositionRouterService(Web3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public PositionRouterService(IWeb3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public Task BasisPointsDivisorQueryAsync(BasisPointsDivisorFunction basisPointsDivisorFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(basisPointsDivisorFunction, blockParameter); + } + + + public Task BasisPointsDivisorQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task AdminQueryAsync(AdminFunction adminFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(adminFunction, blockParameter); + } + + + public Task AdminQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task ApproveRequestAsync(ApproveFunction approveFunction) + { + return ContractHandler.SendRequestAsync(approveFunction); + } + + public Task ApproveRequestAndWaitForReceiptAsync(ApproveFunction approveFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(approveFunction, cancellationToken); + } + + public Task ApproveRequestAsync(string token, string spender, BigInteger amount) + { + var approveFunction = new ApproveFunction(); + approveFunction.Token = token; + approveFunction.Spender = spender; + approveFunction.Amount = amount; + + return ContractHandler.SendRequestAsync(approveFunction); + } + + public Task ApproveRequestAndWaitForReceiptAsync(string token, string spender, BigInteger amount, CancellationTokenSource cancellationToken = null) + { + var approveFunction = new ApproveFunction(); + approveFunction.Token = token; + approveFunction.Spender = spender; + approveFunction.Amount = amount; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(approveFunction, cancellationToken); + } + + public Task CallbackGasLimitQueryAsync(CallbackGasLimitFunction callbackGasLimitFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(callbackGasLimitFunction, blockParameter); + } + + + public Task CallbackGasLimitQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task CancelDecreasePositionRequestAsync(CancelDecreasePositionFunction cancelDecreasePositionFunction) + { + return ContractHandler.SendRequestAsync(cancelDecreasePositionFunction); + } + + public Task CancelDecreasePositionRequestAndWaitForReceiptAsync(CancelDecreasePositionFunction cancelDecreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelDecreasePositionFunction, cancellationToken); + } + + public Task CancelDecreasePositionRequestAsync(byte[] key, string executionFeeReceiver) + { + var cancelDecreasePositionFunction = new CancelDecreasePositionFunction(); + cancelDecreasePositionFunction.Key = key; + cancelDecreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAsync(cancelDecreasePositionFunction); + } + + public Task CancelDecreasePositionRequestAndWaitForReceiptAsync(byte[] key, string executionFeeReceiver, CancellationTokenSource cancellationToken = null) + { + var cancelDecreasePositionFunction = new CancelDecreasePositionFunction(); + cancelDecreasePositionFunction.Key = key; + cancelDecreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelDecreasePositionFunction, cancellationToken); + } + + public Task CancelIncreasePositionRequestAsync(CancelIncreasePositionFunction cancelIncreasePositionFunction) + { + return ContractHandler.SendRequestAsync(cancelIncreasePositionFunction); + } + + public Task CancelIncreasePositionRequestAndWaitForReceiptAsync(CancelIncreasePositionFunction cancelIncreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelIncreasePositionFunction, cancellationToken); + } + + public Task CancelIncreasePositionRequestAsync(byte[] key, string executionFeeReceiver) + { + var cancelIncreasePositionFunction = new CancelIncreasePositionFunction(); + cancelIncreasePositionFunction.Key = key; + cancelIncreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAsync(cancelIncreasePositionFunction); + } + + public Task CancelIncreasePositionRequestAndWaitForReceiptAsync(byte[] key, string executionFeeReceiver, CancellationTokenSource cancellationToken = null) + { + var cancelIncreasePositionFunction = new CancelIncreasePositionFunction(); + cancelIncreasePositionFunction.Key = key; + cancelIncreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(cancelIncreasePositionFunction, cancellationToken); + } + + public Task CreateDecreasePositionRequestAsync(CreateDecreasePositionFunction createDecreasePositionFunction) + { + return ContractHandler.SendRequestAsync(createDecreasePositionFunction); + } + + public Task CreateDecreasePositionRequestAndWaitForReceiptAsync(CreateDecreasePositionFunction createDecreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(createDecreasePositionFunction, cancellationToken); + } + + public Task CreateDecreasePositionRequestAsync(List path, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger acceptablePrice, BigInteger minOut, BigInteger executionFee, bool withdrawETH, string callbackTarget) + { + var createDecreasePositionFunction = new CreateDecreasePositionFunction(); + createDecreasePositionFunction.Path = path; + createDecreasePositionFunction.IndexToken = indexToken; + createDecreasePositionFunction.CollateralDelta = collateralDelta; + createDecreasePositionFunction.SizeDelta = sizeDelta; + createDecreasePositionFunction.IsLong = isLong; + createDecreasePositionFunction.Receiver = receiver; + createDecreasePositionFunction.AcceptablePrice = acceptablePrice; + createDecreasePositionFunction.MinOut = minOut; + createDecreasePositionFunction.ExecutionFee = executionFee; + createDecreasePositionFunction.WithdrawETH = withdrawETH; + createDecreasePositionFunction.CallbackTarget = callbackTarget; + + return ContractHandler.SendRequestAsync(createDecreasePositionFunction); + } + + public Task CreateDecreasePositionRequestAndWaitForReceiptAsync(List path, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger acceptablePrice, BigInteger minOut, BigInteger executionFee, bool withdrawETH, string callbackTarget, CancellationTokenSource cancellationToken = null) + { + var createDecreasePositionFunction = new CreateDecreasePositionFunction(); + createDecreasePositionFunction.Path = path; + createDecreasePositionFunction.IndexToken = indexToken; + createDecreasePositionFunction.CollateralDelta = collateralDelta; + createDecreasePositionFunction.SizeDelta = sizeDelta; + createDecreasePositionFunction.IsLong = isLong; + createDecreasePositionFunction.Receiver = receiver; + createDecreasePositionFunction.AcceptablePrice = acceptablePrice; + createDecreasePositionFunction.MinOut = minOut; + createDecreasePositionFunction.ExecutionFee = executionFee; + createDecreasePositionFunction.WithdrawETH = withdrawETH; + createDecreasePositionFunction.CallbackTarget = callbackTarget; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(createDecreasePositionFunction, cancellationToken); + } + + public Task CreateIncreasePositionRequestAsync(CreateIncreasePositionFunction createIncreasePositionFunction) + { + return ContractHandler.SendRequestAsync(createIncreasePositionFunction); + } + + public Task CreateIncreasePositionRequestAndWaitForReceiptAsync(CreateIncreasePositionFunction createIncreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(createIncreasePositionFunction, cancellationToken); + } + + public Task CreateIncreasePositionRequestAsync(List path, string indexToken, BigInteger amountIn, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger acceptablePrice, BigInteger executionFee, byte[] referralCode, string callbackTarget) + { + var createIncreasePositionFunction = new CreateIncreasePositionFunction(); + createIncreasePositionFunction.Path = path; + createIncreasePositionFunction.IndexToken = indexToken; + createIncreasePositionFunction.AmountIn = amountIn; + createIncreasePositionFunction.MinOut = minOut; + createIncreasePositionFunction.SizeDelta = sizeDelta; + createIncreasePositionFunction.IsLong = isLong; + createIncreasePositionFunction.AcceptablePrice = acceptablePrice; + createIncreasePositionFunction.ExecutionFee = executionFee; + createIncreasePositionFunction.ReferralCode = referralCode; + createIncreasePositionFunction.CallbackTarget = callbackTarget; + + return ContractHandler.SendRequestAsync(createIncreasePositionFunction); + } + + public Task CreateIncreasePositionRequestAndWaitForReceiptAsync(List path, string indexToken, BigInteger amountIn, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger acceptablePrice, BigInteger executionFee, byte[] referralCode, string callbackTarget, CancellationTokenSource cancellationToken = null) + { + var createIncreasePositionFunction = new CreateIncreasePositionFunction(); + createIncreasePositionFunction.Path = path; + createIncreasePositionFunction.IndexToken = indexToken; + createIncreasePositionFunction.AmountIn = amountIn; + createIncreasePositionFunction.MinOut = minOut; + createIncreasePositionFunction.SizeDelta = sizeDelta; + createIncreasePositionFunction.IsLong = isLong; + createIncreasePositionFunction.AcceptablePrice = acceptablePrice; + createIncreasePositionFunction.ExecutionFee = executionFee; + createIncreasePositionFunction.ReferralCode = referralCode; + createIncreasePositionFunction.CallbackTarget = callbackTarget; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(createIncreasePositionFunction, cancellationToken); + } + + public Task CreateIncreasePositionETHRequestAsync(CreateIncreasePositionETHFunction createIncreasePositionETHFunction) + { + return ContractHandler.SendRequestAsync(createIncreasePositionETHFunction); + } + + public Task CreateIncreasePositionETHRequestAndWaitForReceiptAsync(CreateIncreasePositionETHFunction createIncreasePositionETHFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(createIncreasePositionETHFunction, cancellationToken); + } + + public Task CreateIncreasePositionETHRequestAsync(List path, string indexToken, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger acceptablePrice, BigInteger executionFee, byte[] referralCode, string callbackTarget) + { + var createIncreasePositionETHFunction = new CreateIncreasePositionETHFunction(); + createIncreasePositionETHFunction.Path = path; + createIncreasePositionETHFunction.IndexToken = indexToken; + createIncreasePositionETHFunction.MinOut = minOut; + createIncreasePositionETHFunction.SizeDelta = sizeDelta; + createIncreasePositionETHFunction.IsLong = isLong; + createIncreasePositionETHFunction.AcceptablePrice = acceptablePrice; + createIncreasePositionETHFunction.ExecutionFee = executionFee; + createIncreasePositionETHFunction.ReferralCode = referralCode; + createIncreasePositionETHFunction.CallbackTarget = callbackTarget; + + return ContractHandler.SendRequestAsync(createIncreasePositionETHFunction); + } + + public Task CreateIncreasePositionETHRequestAndWaitForReceiptAsync(List path, string indexToken, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger acceptablePrice, BigInteger executionFee, byte[] referralCode, string callbackTarget, CancellationTokenSource cancellationToken = null) + { + var createIncreasePositionETHFunction = new CreateIncreasePositionETHFunction(); + createIncreasePositionETHFunction.Path = path; + createIncreasePositionETHFunction.IndexToken = indexToken; + createIncreasePositionETHFunction.MinOut = minOut; + createIncreasePositionETHFunction.SizeDelta = sizeDelta; + createIncreasePositionETHFunction.IsLong = isLong; + createIncreasePositionETHFunction.AcceptablePrice = acceptablePrice; + createIncreasePositionETHFunction.ExecutionFee = executionFee; + createIncreasePositionETHFunction.ReferralCode = referralCode; + createIncreasePositionETHFunction.CallbackTarget = callbackTarget; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(createIncreasePositionETHFunction, cancellationToken); + } + + public Task DecreasePositionRequestKeysQueryAsync(DecreasePositionRequestKeysFunction decreasePositionRequestKeysFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(decreasePositionRequestKeysFunction, blockParameter); + } + + + public Task DecreasePositionRequestKeysQueryAsync(BigInteger returnValue1, BlockParameter blockParameter = null) + { + var decreasePositionRequestKeysFunction = new DecreasePositionRequestKeysFunction(); + decreasePositionRequestKeysFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(decreasePositionRequestKeysFunction, blockParameter); + } + + public Task DecreasePositionRequestKeysStartQueryAsync(DecreasePositionRequestKeysStartFunction decreasePositionRequestKeysStartFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(decreasePositionRequestKeysStartFunction, blockParameter); + } + + + public Task DecreasePositionRequestKeysStartQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task DecreasePositionRequestsQueryAsync(DecreasePositionRequestsFunction decreasePositionRequestsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(decreasePositionRequestsFunction, blockParameter); + } + + public Task DecreasePositionRequestsQueryAsync(byte[] returnValue1, BlockParameter blockParameter = null) + { + var decreasePositionRequestsFunction = new DecreasePositionRequestsFunction(); + decreasePositionRequestsFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryDeserializingToObjectAsync(decreasePositionRequestsFunction, blockParameter); + } + + public Task DecreasePositionsIndexQueryAsync(DecreasePositionsIndexFunction decreasePositionsIndexFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(decreasePositionsIndexFunction, blockParameter); + } + + + public Task DecreasePositionsIndexQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var decreasePositionsIndexFunction = new DecreasePositionsIndexFunction(); + decreasePositionsIndexFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(decreasePositionsIndexFunction, blockParameter); + } + + public Task DepositFeeQueryAsync(DepositFeeFunction depositFeeFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(depositFeeFunction, blockParameter); + } + + + public Task DepositFeeQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task ExecuteDecreasePositionRequestAsync(ExecuteDecreasePositionFunction executeDecreasePositionFunction) + { + return ContractHandler.SendRequestAsync(executeDecreasePositionFunction); + } + + public Task ExecuteDecreasePositionRequestAndWaitForReceiptAsync(ExecuteDecreasePositionFunction executeDecreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeDecreasePositionFunction, cancellationToken); + } + + public Task ExecuteDecreasePositionRequestAsync(byte[] key, string executionFeeReceiver) + { + var executeDecreasePositionFunction = new ExecuteDecreasePositionFunction(); + executeDecreasePositionFunction.Key = key; + executeDecreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAsync(executeDecreasePositionFunction); + } + + public Task ExecuteDecreasePositionRequestAndWaitForReceiptAsync(byte[] key, string executionFeeReceiver, CancellationTokenSource cancellationToken = null) + { + var executeDecreasePositionFunction = new ExecuteDecreasePositionFunction(); + executeDecreasePositionFunction.Key = key; + executeDecreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeDecreasePositionFunction, cancellationToken); + } + + public Task ExecuteDecreasePositionsRequestAsync(ExecuteDecreasePositionsFunction executeDecreasePositionsFunction) + { + return ContractHandler.SendRequestAsync(executeDecreasePositionsFunction); + } + + public Task ExecuteDecreasePositionsRequestAndWaitForReceiptAsync(ExecuteDecreasePositionsFunction executeDecreasePositionsFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeDecreasePositionsFunction, cancellationToken); + } + + public Task ExecuteDecreasePositionsRequestAsync(BigInteger endIndex, string executionFeeReceiver) + { + var executeDecreasePositionsFunction = new ExecuteDecreasePositionsFunction(); + executeDecreasePositionsFunction.EndIndex = endIndex; + executeDecreasePositionsFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAsync(executeDecreasePositionsFunction); + } + + public Task ExecuteDecreasePositionsRequestAndWaitForReceiptAsync(BigInteger endIndex, string executionFeeReceiver, CancellationTokenSource cancellationToken = null) + { + var executeDecreasePositionsFunction = new ExecuteDecreasePositionsFunction(); + executeDecreasePositionsFunction.EndIndex = endIndex; + executeDecreasePositionsFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeDecreasePositionsFunction, cancellationToken); + } + + public Task ExecuteIncreasePositionRequestAsync(ExecuteIncreasePositionFunction executeIncreasePositionFunction) + { + return ContractHandler.SendRequestAsync(executeIncreasePositionFunction); + } + + public Task ExecuteIncreasePositionRequestAndWaitForReceiptAsync(ExecuteIncreasePositionFunction executeIncreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeIncreasePositionFunction, cancellationToken); + } + + public Task ExecuteIncreasePositionRequestAsync(byte[] key, string executionFeeReceiver) + { + var executeIncreasePositionFunction = new ExecuteIncreasePositionFunction(); + executeIncreasePositionFunction.Key = key; + executeIncreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAsync(executeIncreasePositionFunction); + } + + public Task ExecuteIncreasePositionRequestAndWaitForReceiptAsync(byte[] key, string executionFeeReceiver, CancellationTokenSource cancellationToken = null) + { + var executeIncreasePositionFunction = new ExecuteIncreasePositionFunction(); + executeIncreasePositionFunction.Key = key; + executeIncreasePositionFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeIncreasePositionFunction, cancellationToken); + } + + public Task ExecuteIncreasePositionsRequestAsync(ExecuteIncreasePositionsFunction executeIncreasePositionsFunction) + { + return ContractHandler.SendRequestAsync(executeIncreasePositionsFunction); + } + + public Task ExecuteIncreasePositionsRequestAndWaitForReceiptAsync(ExecuteIncreasePositionsFunction executeIncreasePositionsFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeIncreasePositionsFunction, cancellationToken); + } + + public Task ExecuteIncreasePositionsRequestAsync(BigInteger endIndex, string executionFeeReceiver) + { + var executeIncreasePositionsFunction = new ExecuteIncreasePositionsFunction(); + executeIncreasePositionsFunction.EndIndex = endIndex; + executeIncreasePositionsFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAsync(executeIncreasePositionsFunction); + } + + public Task ExecuteIncreasePositionsRequestAndWaitForReceiptAsync(BigInteger endIndex, string executionFeeReceiver, CancellationTokenSource cancellationToken = null) + { + var executeIncreasePositionsFunction = new ExecuteIncreasePositionsFunction(); + executeIncreasePositionsFunction.EndIndex = endIndex; + executeIncreasePositionsFunction.ExecutionFeeReceiver = executionFeeReceiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(executeIncreasePositionsFunction, cancellationToken); + } + + public Task FeeReservesQueryAsync(FeeReservesFunction feeReservesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(feeReservesFunction, blockParameter); + } + + + public Task FeeReservesQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var feeReservesFunction = new FeeReservesFunction(); + feeReservesFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(feeReservesFunction, blockParameter); + } + + public Task> GetDecreasePositionRequestPathQueryAsync(GetDecreasePositionRequestPathFunction getDecreasePositionRequestPathFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getDecreasePositionRequestPathFunction, blockParameter); + } + + + public Task> GetDecreasePositionRequestPathQueryAsync(byte[] key, BlockParameter blockParameter = null) + { + var getDecreasePositionRequestPathFunction = new GetDecreasePositionRequestPathFunction(); + getDecreasePositionRequestPathFunction.Key = key; + + return ContractHandler.QueryAsync>(getDecreasePositionRequestPathFunction, blockParameter); + } + + public Task> GetIncreasePositionRequestPathQueryAsync(GetIncreasePositionRequestPathFunction getIncreasePositionRequestPathFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getIncreasePositionRequestPathFunction, blockParameter); + } + + + public Task> GetIncreasePositionRequestPathQueryAsync(byte[] key, BlockParameter blockParameter = null) + { + var getIncreasePositionRequestPathFunction = new GetIncreasePositionRequestPathFunction(); + getIncreasePositionRequestPathFunction.Key = key; + + return ContractHandler.QueryAsync>(getIncreasePositionRequestPathFunction, blockParameter); + } + + public Task GetRequestKeyQueryAsync(GetRequestKeyFunction getRequestKeyFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(getRequestKeyFunction, blockParameter); + } + + + public Task GetRequestKeyQueryAsync(string account, BigInteger index, BlockParameter blockParameter = null) + { + var getRequestKeyFunction = new GetRequestKeyFunction(); + getRequestKeyFunction.Account = account; + getRequestKeyFunction.Index = index; + + return ContractHandler.QueryAsync(getRequestKeyFunction, blockParameter); + } + + public Task GetRequestQueueLengthsQueryAsync(GetRequestQueueLengthsFunction getRequestQueueLengthsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getRequestQueueLengthsFunction, blockParameter); + } + + public Task GetRequestQueueLengthsQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(null, blockParameter); + } + + public Task GovQueryAsync(GovFunction govFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(govFunction, blockParameter); + } + + + public Task GovQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task IncreasePositionBufferBpsQueryAsync(IncreasePositionBufferBpsFunction increasePositionBufferBpsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(increasePositionBufferBpsFunction, blockParameter); + } + + + public Task IncreasePositionBufferBpsQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task IncreasePositionRequestKeysQueryAsync(IncreasePositionRequestKeysFunction increasePositionRequestKeysFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(increasePositionRequestKeysFunction, blockParameter); + } + + + public Task IncreasePositionRequestKeysQueryAsync(BigInteger returnValue1, BlockParameter blockParameter = null) + { + var increasePositionRequestKeysFunction = new IncreasePositionRequestKeysFunction(); + increasePositionRequestKeysFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(increasePositionRequestKeysFunction, blockParameter); + } + + public Task IncreasePositionRequestKeysStartQueryAsync(IncreasePositionRequestKeysStartFunction increasePositionRequestKeysStartFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(increasePositionRequestKeysStartFunction, blockParameter); + } + + + public Task IncreasePositionRequestKeysStartQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task IncreasePositionRequestsQueryAsync(IncreasePositionRequestsFunction increasePositionRequestsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(increasePositionRequestsFunction, blockParameter); + } + + public Task IncreasePositionRequestsQueryAsync(byte[] returnValue1, BlockParameter blockParameter = null) + { + var increasePositionRequestsFunction = new IncreasePositionRequestsFunction(); + increasePositionRequestsFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryDeserializingToObjectAsync(increasePositionRequestsFunction, blockParameter); + } + + public Task IncreasePositionsIndexQueryAsync(IncreasePositionsIndexFunction increasePositionsIndexFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(increasePositionsIndexFunction, blockParameter); + } + + + public Task IncreasePositionsIndexQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var increasePositionsIndexFunction = new IncreasePositionsIndexFunction(); + increasePositionsIndexFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(increasePositionsIndexFunction, blockParameter); + } + + public Task IsLeverageEnabledQueryAsync(IsLeverageEnabledFunction isLeverageEnabledFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(isLeverageEnabledFunction, blockParameter); + } + + + public Task IsLeverageEnabledQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task IsPositionKeeperQueryAsync(IsPositionKeeperFunction isPositionKeeperFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(isPositionKeeperFunction, blockParameter); + } + + + public Task IsPositionKeeperQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var isPositionKeeperFunction = new IsPositionKeeperFunction(); + isPositionKeeperFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(isPositionKeeperFunction, blockParameter); + } + + public Task MaxGlobalLongSizesQueryAsync(MaxGlobalLongSizesFunction maxGlobalLongSizesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(maxGlobalLongSizesFunction, blockParameter); + } + + + public Task MaxGlobalLongSizesQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var maxGlobalLongSizesFunction = new MaxGlobalLongSizesFunction(); + maxGlobalLongSizesFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(maxGlobalLongSizesFunction, blockParameter); + } + + public Task MaxGlobalShortSizesQueryAsync(MaxGlobalShortSizesFunction maxGlobalShortSizesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(maxGlobalShortSizesFunction, blockParameter); + } + + + public Task MaxGlobalShortSizesQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var maxGlobalShortSizesFunction = new MaxGlobalShortSizesFunction(); + maxGlobalShortSizesFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(maxGlobalShortSizesFunction, blockParameter); + } + + public Task MaxTimeDelayQueryAsync(MaxTimeDelayFunction maxTimeDelayFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(maxTimeDelayFunction, blockParameter); + } + + + public Task MaxTimeDelayQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task MinBlockDelayKeeperQueryAsync(MinBlockDelayKeeperFunction minBlockDelayKeeperFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(minBlockDelayKeeperFunction, blockParameter); + } + + + public Task MinBlockDelayKeeperQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task MinExecutionFeeQueryAsync(MinExecutionFeeFunction minExecutionFeeFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(minExecutionFeeFunction, blockParameter); + } + + + public Task MinExecutionFeeQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task MinTimeDelayPublicQueryAsync(MinTimeDelayPublicFunction minTimeDelayPublicFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(minTimeDelayPublicFunction, blockParameter); + } + + + public Task MinTimeDelayPublicQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task ReferralStorageQueryAsync(ReferralStorageFunction referralStorageFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(referralStorageFunction, blockParameter); + } + + + public Task ReferralStorageQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task RouterQueryAsync(RouterFunction routerFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(routerFunction, blockParameter); + } + + + public Task RouterQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task SendValueRequestAsync(SendValueFunction sendValueFunction) + { + return ContractHandler.SendRequestAsync(sendValueFunction); + } + + public Task SendValueRequestAndWaitForReceiptAsync(SendValueFunction sendValueFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(sendValueFunction, cancellationToken); + } + + public Task SendValueRequestAsync(string receiver, BigInteger amount) + { + var sendValueFunction = new SendValueFunction(); + sendValueFunction.Receiver = receiver; + sendValueFunction.Amount = amount; + + return ContractHandler.SendRequestAsync(sendValueFunction); + } + + public Task SendValueRequestAndWaitForReceiptAsync(string receiver, BigInteger amount, CancellationTokenSource cancellationToken = null) + { + var sendValueFunction = new SendValueFunction(); + sendValueFunction.Receiver = receiver; + sendValueFunction.Amount = amount; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(sendValueFunction, cancellationToken); + } + + public Task SetAdminRequestAsync(SetAdminFunction setAdminFunction) + { + return ContractHandler.SendRequestAsync(setAdminFunction); + } + + public Task SetAdminRequestAndWaitForReceiptAsync(SetAdminFunction setAdminFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setAdminFunction, cancellationToken); + } + + public Task SetAdminRequestAsync(string admin) + { + var setAdminFunction = new SetAdminFunction(); + setAdminFunction.Admin = admin; + + return ContractHandler.SendRequestAsync(setAdminFunction); + } + + public Task SetAdminRequestAndWaitForReceiptAsync(string admin, CancellationTokenSource cancellationToken = null) + { + var setAdminFunction = new SetAdminFunction(); + setAdminFunction.Admin = admin; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setAdminFunction, cancellationToken); + } + + public Task SetCallbackGasLimitRequestAsync(SetCallbackGasLimitFunction setCallbackGasLimitFunction) + { + return ContractHandler.SendRequestAsync(setCallbackGasLimitFunction); + } + + public Task SetCallbackGasLimitRequestAndWaitForReceiptAsync(SetCallbackGasLimitFunction setCallbackGasLimitFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setCallbackGasLimitFunction, cancellationToken); + } + + public Task SetCallbackGasLimitRequestAsync(BigInteger callbackGasLimit) + { + var setCallbackGasLimitFunction = new SetCallbackGasLimitFunction(); + setCallbackGasLimitFunction.CallbackGasLimit = callbackGasLimit; + + return ContractHandler.SendRequestAsync(setCallbackGasLimitFunction); + } + + public Task SetCallbackGasLimitRequestAndWaitForReceiptAsync(BigInteger callbackGasLimit, CancellationTokenSource cancellationToken = null) + { + var setCallbackGasLimitFunction = new SetCallbackGasLimitFunction(); + setCallbackGasLimitFunction.CallbackGasLimit = callbackGasLimit; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setCallbackGasLimitFunction, cancellationToken); + } + + public Task SetDelayValuesRequestAsync(SetDelayValuesFunction setDelayValuesFunction) + { + return ContractHandler.SendRequestAsync(setDelayValuesFunction); + } + + public Task SetDelayValuesRequestAndWaitForReceiptAsync(SetDelayValuesFunction setDelayValuesFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setDelayValuesFunction, cancellationToken); + } + + public Task SetDelayValuesRequestAsync(BigInteger minBlockDelayKeeper, BigInteger minTimeDelayPublic, BigInteger maxTimeDelay) + { + var setDelayValuesFunction = new SetDelayValuesFunction(); + setDelayValuesFunction.MinBlockDelayKeeper = minBlockDelayKeeper; + setDelayValuesFunction.MinTimeDelayPublic = minTimeDelayPublic; + setDelayValuesFunction.MaxTimeDelay = maxTimeDelay; + + return ContractHandler.SendRequestAsync(setDelayValuesFunction); + } + + public Task SetDelayValuesRequestAndWaitForReceiptAsync(BigInteger minBlockDelayKeeper, BigInteger minTimeDelayPublic, BigInteger maxTimeDelay, CancellationTokenSource cancellationToken = null) + { + var setDelayValuesFunction = new SetDelayValuesFunction(); + setDelayValuesFunction.MinBlockDelayKeeper = minBlockDelayKeeper; + setDelayValuesFunction.MinTimeDelayPublic = minTimeDelayPublic; + setDelayValuesFunction.MaxTimeDelay = maxTimeDelay; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setDelayValuesFunction, cancellationToken); + } + + public Task SetDepositFeeRequestAsync(SetDepositFeeFunction setDepositFeeFunction) + { + return ContractHandler.SendRequestAsync(setDepositFeeFunction); + } + + public Task SetDepositFeeRequestAndWaitForReceiptAsync(SetDepositFeeFunction setDepositFeeFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setDepositFeeFunction, cancellationToken); + } + + public Task SetDepositFeeRequestAsync(BigInteger depositFee) + { + var setDepositFeeFunction = new SetDepositFeeFunction(); + setDepositFeeFunction.DepositFee = depositFee; + + return ContractHandler.SendRequestAsync(setDepositFeeFunction); + } + + public Task SetDepositFeeRequestAndWaitForReceiptAsync(BigInteger depositFee, CancellationTokenSource cancellationToken = null) + { + var setDepositFeeFunction = new SetDepositFeeFunction(); + setDepositFeeFunction.DepositFee = depositFee; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setDepositFeeFunction, cancellationToken); + } + + public Task SetGovRequestAsync(SetGovFunction setGovFunction) + { + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(SetGovFunction setGovFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + + public Task SetGovRequestAsync(string gov) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(string gov, CancellationTokenSource cancellationToken = null) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + + public Task SetIncreasePositionBufferBpsRequestAsync(SetIncreasePositionBufferBpsFunction setIncreasePositionBufferBpsFunction) + { + return ContractHandler.SendRequestAsync(setIncreasePositionBufferBpsFunction); + } + + public Task SetIncreasePositionBufferBpsRequestAndWaitForReceiptAsync(SetIncreasePositionBufferBpsFunction setIncreasePositionBufferBpsFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setIncreasePositionBufferBpsFunction, cancellationToken); + } + + public Task SetIncreasePositionBufferBpsRequestAsync(BigInteger increasePositionBufferBps) + { + var setIncreasePositionBufferBpsFunction = new SetIncreasePositionBufferBpsFunction(); + setIncreasePositionBufferBpsFunction.IncreasePositionBufferBps = increasePositionBufferBps; + + return ContractHandler.SendRequestAsync(setIncreasePositionBufferBpsFunction); + } + + public Task SetIncreasePositionBufferBpsRequestAndWaitForReceiptAsync(BigInteger increasePositionBufferBps, CancellationTokenSource cancellationToken = null) + { + var setIncreasePositionBufferBpsFunction = new SetIncreasePositionBufferBpsFunction(); + setIncreasePositionBufferBpsFunction.IncreasePositionBufferBps = increasePositionBufferBps; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setIncreasePositionBufferBpsFunction, cancellationToken); + } + + public Task SetIsLeverageEnabledRequestAsync(SetIsLeverageEnabledFunction setIsLeverageEnabledFunction) + { + return ContractHandler.SendRequestAsync(setIsLeverageEnabledFunction); + } + + public Task SetIsLeverageEnabledRequestAndWaitForReceiptAsync(SetIsLeverageEnabledFunction setIsLeverageEnabledFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setIsLeverageEnabledFunction, cancellationToken); + } + + public Task SetIsLeverageEnabledRequestAsync(bool isLeverageEnabled) + { + var setIsLeverageEnabledFunction = new SetIsLeverageEnabledFunction(); + setIsLeverageEnabledFunction.IsLeverageEnabled = isLeverageEnabled; + + return ContractHandler.SendRequestAsync(setIsLeverageEnabledFunction); + } + + public Task SetIsLeverageEnabledRequestAndWaitForReceiptAsync(bool isLeverageEnabled, CancellationTokenSource cancellationToken = null) + { + var setIsLeverageEnabledFunction = new SetIsLeverageEnabledFunction(); + setIsLeverageEnabledFunction.IsLeverageEnabled = isLeverageEnabled; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setIsLeverageEnabledFunction, cancellationToken); + } + + public Task SetMaxGlobalSizesRequestAsync(SetMaxGlobalSizesFunction setMaxGlobalSizesFunction) + { + return ContractHandler.SendRequestAsync(setMaxGlobalSizesFunction); + } + + public Task SetMaxGlobalSizesRequestAndWaitForReceiptAsync(SetMaxGlobalSizesFunction setMaxGlobalSizesFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMaxGlobalSizesFunction, cancellationToken); + } + + public Task SetMaxGlobalSizesRequestAsync(List tokens, List longSizes, List shortSizes) + { + var setMaxGlobalSizesFunction = new SetMaxGlobalSizesFunction(); + setMaxGlobalSizesFunction.Tokens = tokens; + setMaxGlobalSizesFunction.LongSizes = longSizes; + setMaxGlobalSizesFunction.ShortSizes = shortSizes; + + return ContractHandler.SendRequestAsync(setMaxGlobalSizesFunction); + } + + public Task SetMaxGlobalSizesRequestAndWaitForReceiptAsync(List tokens, List longSizes, List shortSizes, CancellationTokenSource cancellationToken = null) + { + var setMaxGlobalSizesFunction = new SetMaxGlobalSizesFunction(); + setMaxGlobalSizesFunction.Tokens = tokens; + setMaxGlobalSizesFunction.LongSizes = longSizes; + setMaxGlobalSizesFunction.ShortSizes = shortSizes; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMaxGlobalSizesFunction, cancellationToken); + } + + public Task SetMinExecutionFeeRequestAsync(SetMinExecutionFeeFunction setMinExecutionFeeFunction) + { + return ContractHandler.SendRequestAsync(setMinExecutionFeeFunction); + } + + public Task SetMinExecutionFeeRequestAndWaitForReceiptAsync(SetMinExecutionFeeFunction setMinExecutionFeeFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMinExecutionFeeFunction, cancellationToken); + } + + public Task SetMinExecutionFeeRequestAsync(BigInteger minExecutionFee) + { + var setMinExecutionFeeFunction = new SetMinExecutionFeeFunction(); + setMinExecutionFeeFunction.MinExecutionFee = minExecutionFee; + + return ContractHandler.SendRequestAsync(setMinExecutionFeeFunction); + } + + public Task SetMinExecutionFeeRequestAndWaitForReceiptAsync(BigInteger minExecutionFee, CancellationTokenSource cancellationToken = null) + { + var setMinExecutionFeeFunction = new SetMinExecutionFeeFunction(); + setMinExecutionFeeFunction.MinExecutionFee = minExecutionFee; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setMinExecutionFeeFunction, cancellationToken); + } + + public Task SetPositionKeeperRequestAsync(SetPositionKeeperFunction setPositionKeeperFunction) + { + return ContractHandler.SendRequestAsync(setPositionKeeperFunction); + } + + public Task SetPositionKeeperRequestAndWaitForReceiptAsync(SetPositionKeeperFunction setPositionKeeperFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setPositionKeeperFunction, cancellationToken); + } + + public Task SetPositionKeeperRequestAsync(string account, bool isActive) + { + var setPositionKeeperFunction = new SetPositionKeeperFunction(); + setPositionKeeperFunction.Account = account; + setPositionKeeperFunction.IsActive = isActive; + + return ContractHandler.SendRequestAsync(setPositionKeeperFunction); + } + + public Task SetPositionKeeperRequestAndWaitForReceiptAsync(string account, bool isActive, CancellationTokenSource cancellationToken = null) + { + var setPositionKeeperFunction = new SetPositionKeeperFunction(); + setPositionKeeperFunction.Account = account; + setPositionKeeperFunction.IsActive = isActive; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setPositionKeeperFunction, cancellationToken); + } + + public Task SetReferralStorageRequestAsync(SetReferralStorageFunction setReferralStorageFunction) + { + return ContractHandler.SendRequestAsync(setReferralStorageFunction); + } + + public Task SetReferralStorageRequestAndWaitForReceiptAsync(SetReferralStorageFunction setReferralStorageFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setReferralStorageFunction, cancellationToken); + } + + public Task SetReferralStorageRequestAsync(string referralStorage) + { + var setReferralStorageFunction = new SetReferralStorageFunction(); + setReferralStorageFunction.ReferralStorage = referralStorage; + + return ContractHandler.SendRequestAsync(setReferralStorageFunction); + } + + public Task SetReferralStorageRequestAndWaitForReceiptAsync(string referralStorage, CancellationTokenSource cancellationToken = null) + { + var setReferralStorageFunction = new SetReferralStorageFunction(); + setReferralStorageFunction.ReferralStorage = referralStorage; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setReferralStorageFunction, cancellationToken); + } + + public Task SetRequestKeysStartValuesRequestAsync(SetRequestKeysStartValuesFunction setRequestKeysStartValuesFunction) + { + return ContractHandler.SendRequestAsync(setRequestKeysStartValuesFunction); + } + + public Task SetRequestKeysStartValuesRequestAndWaitForReceiptAsync(SetRequestKeysStartValuesFunction setRequestKeysStartValuesFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setRequestKeysStartValuesFunction, cancellationToken); + } + + public Task SetRequestKeysStartValuesRequestAsync(BigInteger increasePositionRequestKeysStart, BigInteger decreasePositionRequestKeysStart) + { + var setRequestKeysStartValuesFunction = new SetRequestKeysStartValuesFunction(); + setRequestKeysStartValuesFunction.IncreasePositionRequestKeysStart = increasePositionRequestKeysStart; + setRequestKeysStartValuesFunction.DecreasePositionRequestKeysStart = decreasePositionRequestKeysStart; + + return ContractHandler.SendRequestAsync(setRequestKeysStartValuesFunction); + } + + public Task SetRequestKeysStartValuesRequestAndWaitForReceiptAsync(BigInteger increasePositionRequestKeysStart, BigInteger decreasePositionRequestKeysStart, CancellationTokenSource cancellationToken = null) + { + var setRequestKeysStartValuesFunction = new SetRequestKeysStartValuesFunction(); + setRequestKeysStartValuesFunction.IncreasePositionRequestKeysStart = increasePositionRequestKeysStart; + setRequestKeysStartValuesFunction.DecreasePositionRequestKeysStart = decreasePositionRequestKeysStart; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setRequestKeysStartValuesFunction, cancellationToken); + } + + public Task ShortsTrackerQueryAsync(ShortsTrackerFunction shortsTrackerFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(shortsTrackerFunction, blockParameter); + } + + + public Task ShortsTrackerQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task VaultQueryAsync(VaultFunction vaultFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(vaultFunction, blockParameter); + } + + + public Task VaultQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task WethQueryAsync(WethFunction wethFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(wethFunction, blockParameter); + } + + + public Task WethQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task WithdrawFeesRequestAsync(WithdrawFeesFunction withdrawFeesFunction) + { + return ContractHandler.SendRequestAsync(withdrawFeesFunction); + } + + public Task WithdrawFeesRequestAndWaitForReceiptAsync(WithdrawFeesFunction withdrawFeesFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(withdrawFeesFunction, cancellationToken); + } + + public Task WithdrawFeesRequestAsync(string token, string receiver) + { + var withdrawFeesFunction = new WithdrawFeesFunction(); + withdrawFeesFunction.Token = token; + withdrawFeesFunction.Receiver = receiver; + + return ContractHandler.SendRequestAsync(withdrawFeesFunction); + } + + public Task WithdrawFeesRequestAndWaitForReceiptAsync(string token, string receiver, CancellationTokenSource cancellationToken = null) + { + var withdrawFeesFunction = new WithdrawFeesFunction(); + withdrawFeesFunction.Token = token; + withdrawFeesFunction.Receiver = receiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(withdrawFeesFunction, cancellationToken); + } + } +} diff --git a/src/Managing.Tools.ABI/Program.cs b/src/Managing.Tools.ABI/Program.cs new file mode 100644 index 0000000..3751555 --- /dev/null +++ b/src/Managing.Tools.ABI/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/src/Managing.Tools.ABI/Reader/ContractDefinition/ReaderDefinition.cs b/src/Managing.Tools.ABI/Reader/ContractDefinition/ReaderDefinition.cs new file mode 100644 index 0000000..3a7117f --- /dev/null +++ b/src/Managing.Tools.ABI/Reader/ContractDefinition/ReaderDefinition.cs @@ -0,0 +1,540 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Managing.Tools.Reader.ContractDefinition +{ + + + public partial class ReaderDeployment : ReaderDeploymentBase + { + public ReaderDeployment() : base(BYTECODE) { } + public ReaderDeployment(string byteCode) : base(byteCode) { } + } + + public class ReaderDeploymentBase : ContractDeploymentMessage + { + public static string BYTECODE = "608060405234801561001057600080fd5b50600080546001600160a01b031916331790556159ff80620000336000396000f3fe608060405234801561001057600080fd5b506004361061013e5760003560e01c8063126082cf1461014357806312d43a511461015d57806320542568146101815780632413c8c1146102925780632ac0184c146103335780632e3e3342146103e45780633613d52714610495578063440f018c1461054657806348211934146105a05780637b906e931461065157806386d4d0f514610712578063870d917c146107c35780638e83ca32146107cb578063937a0be81461088c57806395082d251461093d57806395a7535a146109455780639b0183c3146109ff578063a4543ead14610a20578063ad7e549714610ad1578063c6f1d67614610ad9578063cfad57a214610af5578063d7176ca914610b1b578063d802178e14610b70578063dc383cab14610c21578063f3535e6c14610ddf578063fbdb05ca14610e17575b600080fd5b61014b610ec8565b60408051918252519081900360200190f35b610165610ece565b604080516001600160a01b039092168252519081900360200190f35b6102426004803603608081101561019757600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b8111156101d157600080fd5b8201836020820111156101e357600080fd5b803590602001918460208302840111600160201b8311171561020457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610edd945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561027e578181015183820152602001610266565b505050509050019250505060405180910390f35b610242600480360360208110156102a857600080fd5b810190602081018135600160201b8111156102c257600080fd5b8201836020820111156102d457600080fd5b803590602001918460208302840111600160201b831117156102f557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506115eb945050505050565b61014b6004803603604081101561034957600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561037357600080fd5b82018360208201111561038557600080fd5b803590602001918460208302840111600160201b831117156103a657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506116e4945050505050565b610242600480360360408110156103fa57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561042457600080fd5b82018360208201111561043657600080fd5b803590602001918460208302840111600160201b8311171561045757600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061180f945050505050565b610242600480360360408110156104ab57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156104d557600080fd5b8201836020820111156104e757600080fd5b803590602001918460208302840111600160201b8311171561050857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611a14945050505050565b6105826004803603608081101561055c57600080fd5b506001600160a01b03813581169160208101358216916040820135169060600135611e4e565b60408051938452602084019290925282820152519081900360600190f35b610242600480360360408110156105b657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156105e057600080fd5b8201836020820111156105f257600080fd5b803590602001918460208302840111600160201b8311171561061357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506123b3945050505050565b6102426004803603608081101561066757600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b8111156106a157600080fd5b8201836020820111156106b357600080fd5b803590602001918460208302840111600160201b831117156106d457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612867945050505050565b6102426004803603604081101561072857600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561075257600080fd5b82018360208201111561076457600080fd5b803590602001918460208302840111600160201b8311171561078557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550613098945050505050565b61014b613198565b610242600480360360808110156107e157600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b81111561081b57600080fd5b82018360208201111561082d57600080fd5b803590602001918460208302840111600160201b8311171561084e57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061319d945050505050565b610242600480360360408110156108a257600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156108cc57600080fd5b8201836020820111156108de57600080fd5b803590602001918460208302840111600160201b831117156108ff57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550613b1a945050505050565b61014b613caf565b6102426004803603606081101561095b57600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b81111561098e57600080fd5b8201836020820111156109a057600080fd5b803590602001918460208302840111600160201b831117156109c157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550613cbf945050505050565b610a1e60048036036020811015610a1557600080fd5b5035151561418d565b005b61024260048036036040811015610a3657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610a6057600080fd5b820183602082011115610a7257600080fd5b803590602001918460208302840111600160201b83111715610a9357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550614202945050505050565b61014b61446e565b610ae1614473565b604080519115158252519081900360200190f35b610a1e60048036036020811015610b0b57600080fd5b50356001600160a01b0316614483565b610b5760048036036080811015610b3157600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356144fc565b6040805192835260208301919091528051918290030190f35b61024260048036036040811015610b8657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610bb057600080fd5b820183602082011115610bc257600080fd5b803590602001918460208302840111600160201b83111715610be357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550614bb2945050505050565b610242600480360360a0811015610c3757600080fd5b6001600160a01b038235811692602081013590911691810190606081016040820135600160201b811115610c6a57600080fd5b820183602082011115610c7c57600080fd5b803590602001918460208302840111600160201b83111715610c9d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610cec57600080fd5b820183602082011115610cfe57600080fd5b803590602001918460208302840111600160201b83111715610d1f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610d6e57600080fd5b820183602082011115610d8057600080fd5b803590602001918460208302840111600160201b83111715610da157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550614cfe945050505050565b61014b60048036036060811015610df557600080fd5b506001600160a01b0381358116916020810135821691604090910135166150fa565b61014b60048036036040811015610e2d57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610e5757600080fd5b820183602082011115610e6957600080fd5b803590602001918460208302840111600160201b83111715610e8a57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506156c1945050505050565b61271081565b6000546001600160a01b031681565b60606000600a905060008690506000816001600160a01b031663741bef1a6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f2557600080fd5b505afa158015610f39573d6000803e3d6000fd5b505050506040513d6020811015610f4f57600080fd5b5051855190915060609084026001600160401b0381118015610f7057600080fd5b50604051908082528060200260200182016040528015610f9a578160200160208202803683370190505b50905060005b86518110156115de576000878281518110610fb757fe5b6020026020010151905060006001600160a01b0316816001600160a01b03161415610fdf5750885b846001600160a01b03166352f55eed826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561102c57600080fd5b505afa158015611040573d6000803e3d6000fd5b505050506040513d602081101561105657600080fd5b50518351849084890290811061106857fe5b602002602001018181525050846001600160a01b031663c3c7b9e9826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156110c157600080fd5b505afa1580156110d5573d6000803e3d6000fd5b505050506040513d60208110156110eb57600080fd5b5051835184906001858a020190811061110057fe5b602002602001018181525050846001600160a01b0316631aa4ace5826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561115957600080fd5b505afa15801561116d573d6000803e3d6000fd5b505050506040513d602081101561118357600080fd5b5051835184906002858a020190811061119857fe5b602002602001018181525050846001600160a01b0316632c668ec1828b6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b1580156111f957600080fd5b505afa15801561120d573d6000803e3d6000fd5b505050506040513d602081101561122357600080fd5b5051835184906003858a020190811061123857fe5b602002602001018181525050846001600160a01b031663ab2f3ad4826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561129157600080fd5b505afa1580156112a5573d6000803e3d6000fd5b505050506040513d60208110156112bb57600080fd5b5051835184906004858a02019081106112d057fe5b602002602001018181525050846001600160a01b03166381a612d6826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561132957600080fd5b505afa15801561133d573d6000803e3d6000fd5b505050506040513d602081101561135357600080fd5b5051835184906005858a020190811061136857fe5b602002602001018181525050846001600160a01b031663e124e6d2826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113c157600080fd5b505afa1580156113d5573d6000803e3d6000fd5b505050506040513d60208110156113eb57600080fd5b5051835184906006858a020190811061140057fe5b602002602001018181525050846001600160a01b031663f07456ce826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561145957600080fd5b505afa15801561146d573d6000803e3d6000fd5b505050506040513d602081101561148357600080fd5b5051835184906007858a020190811061149857fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600060248301529151918716926356c8c2c192604480840193829003018186803b1580156114f257600080fd5b505afa158015611506573d6000803e3d6000fd5b505050506040513d602081101561151c57600080fd5b5051835184906008858a020190811061153157fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600160248301529151918716926356c8c2c192604480840193829003018186803b15801561158b57600080fd5b505afa15801561159f573d6000803e3d6000fd5b505050506040513d60208110156115b557600080fd5b5051835184906009858a02019081106115ca57fe5b602090810291909101015250600101610fa0565b5098975050505050505050565b60608082516001600160401b038111801561160557600080fd5b5060405190808252806020026020018201604052801561162f578160200160208202803683370190505b50905060005b83518110156116dd57600084828151811061164c57fe5b60200260200101519050806001600160a01b031663817b1cd26040518163ffffffff1660e01b815260040160206040518083038186803b15801561168f57600080fd5b505afa1580156116a3573d6000803e3d6000fd5b505050506040513d60208110156116b957600080fd5b505183518490849081106116c957fe5b602090810291909101015250600101611635565b5092915050565b600080836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561172057600080fd5b505afa158015611734573d6000803e3d6000fd5b505050506040513d602081101561174a57600080fd5b5051905060005b835181101561180557600084828151811061176857fe5b602002602001015190506000866001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156117c157600080fd5b505afa1580156117d5573d6000803e3d6000fd5b505050506040513d60208110156117eb57600080fd5b505190506117f9848261577a565b93505050600101611751565b5090505b92915050565b60606000600290506060818451026001600160401b038111801561183257600080fd5b5060405190808252806020026020018201604052801561185c578160200160208202803683370190505b50905060005b8451811015611a0b57600085828151811061187957fe5b6020026020010151905060006001600160a01b0316816001600160a01b031614156118e857866001600160a01b03163183858402815181106118b757fe5b602002602001018181525050600083858402600101815181106118d657fe5b60200260200101818152505050611a03565b806001600160a01b03166370a08231886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561193557600080fd5b505afa158015611949573d6000803e3d6000fd5b505050506040513d602081101561195f57600080fd5b50518351849084870290811061197157fe5b602002602001018181525050806001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119b657600080fd5b505afa1580156119ca573d6000803e3d6000fd5b505050506040513d60208110156119e057600080fd5b5051835184906001858802019081106119f557fe5b602002602001018181525050505b600101611862565b50949350505050565b60606000600690506060818451026001600160401b0381118015611a3757600080fd5b50604051908082528060200260200182016040528015611a61578160200160208202803683370190505b50905060005b8451811015611a0b576000858281518110611a7e57fe5b602090810291909101810151604080516317e1d38560e11b81526001600160a01b0380841660048301526001602483018190526044830152600060648301529151929450908a1692632fc3a70a92608480840193829003018186803b158015611ae657600080fd5b505afa158015611afa573d6000803e3d6000fd5b505050506040513d6020811015611b1057600080fd5b505183518490848702908110611b2257fe5b602090810291909101810191909152604080516317e1d38560e11b81526001600160a01b0384811660048301526000602483018190526001604484015260648301529151918a1692632fc3a70a92608480840193829003018186803b158015611b8a57600080fd5b505afa158015611b9e573d6000803e3d6000fd5b505050506040513d6020811015611bb457600080fd5b505183518490600185880201908110611bc957fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600160248301529151918a16926356c8c2c192604480840193829003018186803b158015611c2357600080fd5b505afa158015611c37573d6000803e3d6000fd5b505050506040513d6020811015611c4d57600080fd5b505183518490600285880201908110611c6257fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600060248301529151918a16926356c8c2c192604480840193829003018186803b158015611cbc57600080fd5b505afa158015611cd0573d6000803e3d6000fd5b505050506040513d6020811015611ce657600080fd5b505183518490600385880201908110611cfb57fe5b602002602001018181525050866001600160a01b0316636ce8a44b826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611d5457600080fd5b505afa158015611d68573d6000803e3d6000fd5b505050506040513d6020811015611d7e57600080fd5b5051611d8b576000611d8e565b60015b60ff168385840260040181518110611da257fe5b602002602001018181525050866001600160a01b03166348cac277826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611dfb57600080fd5b505afa158015611e0f573d6000803e3d6000fd5b505050506040513d6020811015611e2557600080fd5b505183518490600585880201908110611e3a57fe5b602090810291909101015250600101611a67565b600080600080876001600160a01b03166381a612d6886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d6020811015611ecb57600080fd5b5051604080516323b95ceb60e21b81526001600160a01b038a811660048301529151929350600092918b1691638ee573ac91602480820192602092909190829003018186803b158015611f1d57600080fd5b505afa158015611f31573d6000803e3d6000fd5b505050506040513d6020811015611f4757600080fd5b505190506000611f6d68327cb2734119d3b7a9601e1b611f6789866157bc565b90615815565b9050611f89600a83900a611f6783670de0b6b3a76400006157bc565b905060008a6001600160a01b03166342b60b038b6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611fda57600080fd5b505afa158015611fee573d6000803e3d6000fd5b505050506040513d602081101561200457600080fd5b5051801561208757508a6001600160a01b03166342b60b038a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561205a57600080fd5b505afa15801561206e573d6000803e3d6000fd5b505050506040513d602081101561208457600080fd5b50515b90506000816120fa578b6001600160a01b031663a22f23926040518163ffffffff1660e01b815260040160206040518083038186803b1580156120c957600080fd5b505afa1580156120dd573d6000803e3d6000fd5b505050506040513d60208110156120f357600080fd5b5051612160565b8b6001600160a01b031663df73a2676040518163ffffffff1660e01b815260040160206040518083038186803b15801561213357600080fd5b505afa158015612147573d6000803e3d6000fd5b505050506040513d602081101561215d57600080fd5b50515b90506000826121d3578c6001600160a01b0316637a210a2b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156121a257600080fd5b505afa1580156121b6573d6000803e3d6000fd5b505050506040513d60208110156121cc57600080fd5b5051612239565b8c6001600160a01b03166310eb56c26040518163ffffffff1660e01b815260040160206040518083038186803b15801561220c57600080fd5b505afa158015612220573d6000803e3d6000fd5b505050506040513d602081101561223657600080fd5b50515b905060008d6001600160a01b031663c7e074c38e87868660016040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b1580156122ad57600080fd5b505afa1580156122c1573d6000803e3d6000fd5b505050506040513d60208110156122d757600080fd5b8101908080519060200190929190505050905060008e6001600160a01b031663c7e074c38e88878760006040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b15801561235c57600080fd5b505afa158015612370573d6000803e3d6000fd5b505050506040513d602081101561238657600080fd5b505190506000818311612399578161239b565b825b9b509199509750505050505050509450945094915050565b60606000600790506060818451026001600160401b03811180156123d657600080fd5b50604051908082528060200260200182016040528015612400578160200160208202803683370190505b50905060005b8451811015611a0b57600085828151811061241d57fe5b60200260200101519050806001600160a01b0316635d50e729886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561247457600080fd5b505afa158015612488573d6000803e3d6000fd5b505050506040513d602081101561249e57600080fd5b5051835184908487029081106124b057fe5b602002602001018181525050806001600160a01b031663d5a73fdd886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561250957600080fd5b505afa15801561251d573d6000803e3d6000fd5b505050506040513d602081101561253357600080fd5b50518351849060018588020190811061254857fe5b60200260200101818152505085828151811061256057fe5b60200260200101516001600160a01b03166370a08231886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156125b457600080fd5b505afa1580156125c8573d6000803e3d6000fd5b505050506040513d60208110156125de57600080fd5b5051835184906002858802019081106125f357fe5b602002602001018181525050806001600160a01b03166371417b32886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561264c57600080fd5b505afa158015612660573d6000803e3d6000fd5b505050506040513d602081101561267657600080fd5b50518351849060038588020190811061268b57fe5b602002602001018181525050806001600160a01b031663402914f5886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156126e457600080fd5b505afa1580156126f8573d6000803e3d6000fd5b505050506040513d602081101561270e57600080fd5b50518351849060048588020190811061272357fe5b602002602001018181525050806001600160a01b03166308f26c76886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561277c57600080fd5b505afa158015612790573d6000803e3d6000fd5b505050506040513d60208110156127a657600080fd5b5051835184906005858802019081106127bb57fe5b602002602001018181525050806001600160a01b03166345f01ee6886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561281457600080fd5b505afa158015612828573d6000803e3d6000fd5b505050506040513d602081101561283e57600080fd5b50518351849060068588020190811061285357fe5b602090810291909101015250600101612406565b60606000600c905060008690506000816001600160a01b031663741bef1a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156128af57600080fd5b505afa1580156128c3573d6000803e3d6000fd5b505050506040513d60208110156128d957600080fd5b5051855190915060609084026001600160401b03811180156128fa57600080fd5b50604051908082528060200260200182016040528015612924578160200160208202803683370190505b50905060005b86518110156115de57600087828151811061294157fe5b6020026020010151905060006001600160a01b0316816001600160a01b031614156129695750885b846001600160a01b03166352f55eed826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156129b657600080fd5b505afa1580156129ca573d6000803e3d6000fd5b505050506040513d60208110156129e057600080fd5b5051835184908489029081106129f257fe5b602002602001018181525050846001600160a01b031663c3c7b9e9826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612a4b57600080fd5b505afa158015612a5f573d6000803e3d6000fd5b505050506040513d6020811015612a7557600080fd5b5051835184906001858a0201908110612a8a57fe5b602002602001018181525050846001600160a01b0316631aa4ace5826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612ae357600080fd5b505afa158015612af7573d6000803e3d6000fd5b505050506040513d6020811015612b0d57600080fd5b5051835184906002858a0201908110612b2257fe5b602002602001018181525050846001600160a01b0316632c668ec1828b6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b158015612b8357600080fd5b505afa158015612b97573d6000803e3d6000fd5b505050506040513d6020811015612bad57600080fd5b5051835184906003858a0201908110612bc257fe5b602002602001018181525050846001600160a01b031663ab2f3ad4826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612c1b57600080fd5b505afa158015612c2f573d6000803e3d6000fd5b505050506040513d6020811015612c4557600080fd5b5051835184906004858a0201908110612c5a57fe5b602002602001018181525050846001600160a01b0316634a993ee9826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612cb357600080fd5b505afa158015612cc7573d6000803e3d6000fd5b505050506040513d6020811015612cdd57600080fd5b5051835184906005858a0201908110612cf257fe5b602002602001018181525050846001600160a01b031663ad1e4f8d826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612d4b57600080fd5b505afa158015612d5f573d6000803e3d6000fd5b505050506040513d6020811015612d7557600080fd5b5051835184906006858a0201908110612d8a57fe5b602002602001018181525050846001600160a01b03166381a612d6826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612de357600080fd5b505afa158015612df7573d6000803e3d6000fd5b505050506040513d6020811015612e0d57600080fd5b5051835184906007858a0201908110612e2257fe5b602002602001018181525050846001600160a01b031663e124e6d2826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612e7b57600080fd5b505afa158015612e8f573d6000803e3d6000fd5b505050506040513d6020811015612ea557600080fd5b5051835184906008858a0201908110612eba57fe5b602002602001018181525050846001600160a01b031663f07456ce826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612f1357600080fd5b505afa158015612f27573d6000803e3d6000fd5b505050506040513d6020811015612f3d57600080fd5b5051835184906009858a0201908110612f5257fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600060248301529151918716926356c8c2c192604480840193829003018186803b158015612fac57600080fd5b505afa158015612fc0573d6000803e3d6000fd5b505050506040513d6020811015612fd657600080fd5b505183518490600a858a0201908110612feb57fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038481166004830152600160248301529151918716926356c8c2c192604480840193829003018186803b15801561304557600080fd5b505afa158015613059573d6000803e3d6000fd5b505050506040513d602081101561306f57600080fd5b505183518490600b858a020190811061308457fe5b60209081029190910101525060010161292a565b60608082516001600160401b03811180156130b257600080fd5b506040519080825280602002602001820160405280156130dc578160200160208202803683370190505b50905060005b835181101561180557846001600160a01b0316631ce9cb8f85838151811061310657fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561314b57600080fd5b505afa15801561315f573d6000803e3d6000fd5b505050506040513d602081101561317557600080fd5b5051825183908390811061318557fe5b60209081029190910101526001016130e2565b601281565b60606000600e905060008690506000816001600160a01b031663741bef1a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156131e557600080fd5b505afa1580156131f9573d6000803e3d6000fd5b505050506040513d602081101561320f57600080fd5b5051855190915060609084026001600160401b038111801561323057600080fd5b5060405190808252806020026020018201604052801561325a578160200160208202803683370190505b50905060005b86518110156115de57600087828151811061327757fe5b6020026020010151905060006001600160a01b0316816001600160a01b0316141561329f5750885b60008054600160a01b900460ff166132b8576000613332565b856001600160a01b0316639698d25a836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561330557600080fd5b505afa158015613319573d6000803e3d6000fd5b505050506040513d602081101561332f57600080fd5b50515b9050856001600160a01b03166352f55eed836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561338157600080fd5b505afa158015613395573d6000803e3d6000fd5b505050506040513d60208110156133ab57600080fd5b505184518590858a029081106133bd57fe5b602002602001018181525050856001600160a01b031663c3c7b9e9836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561341657600080fd5b505afa15801561342a573d6000803e3d6000fd5b505050506040513d602081101561344057600080fd5b5051845185906001868b020190811061345557fe5b602002602001018181525050856001600160a01b0316631aa4ace5836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d60208110156134d857600080fd5b5051845185906002868b02019081106134ed57fe5b602002602001018181525050856001600160a01b0316632c668ec1838c6040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b15801561354e57600080fd5b505afa158015613562573d6000803e3d6000fd5b505050506040513d602081101561357857600080fd5b5051845185906003868b020190811061358d57fe5b602002602001018181525050856001600160a01b031663ab2f3ad4836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156135e657600080fd5b505afa1580156135fa573d6000803e3d6000fd5b505050506040513d602081101561361057600080fd5b5051845185906004868b020190811061362557fe5b602002602001018181525050856001600160a01b0316634a993ee9836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561367e57600080fd5b505afa158015613692573d6000803e3d6000fd5b505050506040513d60208110156136a857600080fd5b5051845185906005868b02019081106136bd57fe5b602002602001018181525050856001600160a01b031663ad1e4f8d836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561371657600080fd5b505afa15801561372a573d6000803e3d6000fd5b505050506040513d602081101561374057600080fd5b5051845185906006868b020190811061375557fe5b602002602001018181525050856001600160a01b0316638a78daa8836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156137ae57600080fd5b505afa1580156137c2573d6000803e3d6000fd5b505050506040513d60208110156137d857600080fd5b5051845185906007868b02019081106137ed57fe5b60200260200101818152505080848885026008018151811061380b57fe5b602002602001018181525050856001600160a01b03166381a612d6836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561386457600080fd5b505afa158015613878573d6000803e3d6000fd5b505050506040513d602081101561388e57600080fd5b5051845185906009868b02019081106138a357fe5b602002602001018181525050856001600160a01b031663e124e6d2836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156138fc57600080fd5b505afa158015613910573d6000803e3d6000fd5b505050506040513d602081101561392657600080fd5b505184518590600a868b020190811061393b57fe5b602002602001018181525050856001600160a01b031663f07456ce836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561399457600080fd5b505afa1580156139a8573d6000803e3d6000fd5b505050506040513d60208110156139be57600080fd5b505184518590600b868b02019081106139d357fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038581166004830152600060248301529151918816926356c8c2c192604480840193829003018186803b158015613a2d57600080fd5b505afa158015613a41573d6000803e3d6000fd5b505050506040513d6020811015613a5757600080fd5b505184518590600c868b0201908110613a6c57fe5b602090810291909101810191909152604080516356c8c2c160e01b81526001600160a01b038581166004830152600160248301529151918816926356c8c2c192604480840193829003018186803b158015613ac657600080fd5b505afa158015613ada573d6000803e3d6000fd5b505050506040513d6020811015613af057600080fd5b505184518590600d868b0201908110613b0557fe5b60209081029190910101525050600101613260565b60606000600290506060818451026001600160401b0381118015613b3d57600080fd5b50604051908082528060200260200182016040528015613b67578160200160208202803683370190505b50905060005b8451811015611a0b576000858281518110613b8457fe5b60200260200101519050806001600160a01b031663402914f5886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613bdb57600080fd5b505afa158015613bef573d6000803e3d6000fd5b505050506040513d6020811015613c0557600080fd5b505183518490848702908110613c1757fe5b602002602001018181525050806001600160a01b0316632459f51d6040518163ffffffff1660e01b815260040160206040518083038186803b158015613c5c57600080fd5b505afa158015613c70573d6000803e3d6000fd5b505050506040513d6020811015613c8657600080fd5b505183518490600185880201908110613c9b57fe5b602090810291909101015250600101613b6d565b68327cb2734119d3b7a9601e1b81565b60606000600290506060818451026001600160401b0381118015613ce257600080fd5b50604051908082528060200260200182016040528015613d0c578160200160208202803683370190505b5090508560005b855181101561417f576000868281518110613d2a57fe5b6020026020010151905060006001600160a01b0316816001600160a01b03161415613d525750865b6000836001600160a01b03166342b60b03836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613da157600080fd5b505afa158015613db5573d6000803e3d6000fd5b505050506040513d6020811015613dcb57600080fd5b5051613e3b57836001600160a01b031663c4f718bf6040518163ffffffff1660e01b815260040160206040518083038186803b158015613e0a57600080fd5b505afa158015613e1e573d6000803e3d6000fd5b505050506040513d6020811015613e3457600080fd5b5051613ea1565b836001600160a01b031663134ca63b6040518163ffffffff1660e01b815260040160206040518083038186803b158015613e7457600080fd5b505afa158015613e88573d6000803e3d6000fd5b505050506040513d6020811015613e9e57600080fd5b50515b90506000846001600160a01b031663c3c7b9e9846040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613ef257600080fd5b505afa158015613f06573d6000803e3d6000fd5b505050506040513d6020811015613f1c57600080fd5b5051604080516352f55eed60e01b81526001600160a01b0386811660048301529151929350600092918816916352f55eed91602480820192602092909190829003018186803b158015613f6e57600080fd5b505afa158015613f82573d6000803e3d6000fd5b505050506040513d6020811015613f9857600080fd5b505190508015613fcb57613fb081611f6785856157bc565b8789870281518110613fbe57fe5b6020026020010181815250505b6000866001600160a01b031663c65bc7b1866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561401a57600080fd5b505afa15801561402e573d6000803e3d6000fd5b505050506040513d602081101561404457600080fd5b5051111561416f576000866001600160a01b031663a93acac2866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561409b57600080fd5b505afa1580156140af573d6000803e3d6000fd5b505050506040513d60208110156140c557600080fd5b50516040805163c65bc7b160e01b81526001600160a01b0388811660048301529151929350600092918a169163c65bc7b191602480820192602092909190829003018186803b15801561411757600080fd5b505afa15801561412b573d6000803e3d6000fd5b505050506040513d602081101561414157600080fd5b5051905061414f8183615854565b898b89026001018151811061416057fe5b60200260200101818152505050505b505060019092019150613d139050565b5090925050505b9392505050565b6000546001600160a01b031633146141e4576040805162461bcd60e51b815260206004820152601560248201527423b7bb32b93730b136329d103337b93134b23232b760591b604482015290519081900360640190fd5b60008054911515600160a01b0260ff60a01b19909216919091179055565b80516060906002908190839082900482026001600160401b038111801561422857600080fd5b50604051908082528060200260200182016040528015614252578160200160208202803683370190505b50905060005b8386518161426257fe5b04811015614464576000868583028151811061427a57fe5b602002602001015190506000878684026001018151811061429757fe5b602002602001015190506000896001600160a01b031663e6a4390584846040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b15801561430157600080fd5b505afa158015614315573d6000803e3d6000fd5b505050506040513d602081101561432b57600080fd5b5051604080516370a0823160e01b81526001600160a01b0380841660048301529151929350908516916370a0823191602480820192602092909190829003018186803b15801561437a57600080fd5b505afa15801561438e573d6000803e3d6000fd5b505050506040513d60208110156143a457600080fd5b5051855186908689029081106143b657fe5b602002602001018181525050816001600160a01b03166370a08231826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561440f57600080fd5b505afa158015614423573d6000803e3d6000fd5b505050506040513d602081101561443957600080fd5b5051855186906001878a020190811061444e57fe5b6020908102919091010152505050600101614258565b5095945050505050565b600981565b600054600160a01b900460ff1681565b6000546001600160a01b031633146144da576040805162461bcd60e51b815260206004820152601560248201527423b7bb32b93730b136329d103337b93134b23232b760591b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000866001600160a01b03166381a612d6876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561454e57600080fd5b505afa158015614562573d6000803e3d6000fd5b505050506040513d602081101561457857600080fd5b5051604080516323b95ceb60e21b81526001600160a01b0389811660048301529151929350600092918a1691638ee573ac91602480820192602092909190829003018186803b1580156145ca57600080fd5b505afa1580156145de573d6000803e3d6000fd5b505050506040513d60208110156145f457600080fd5b5051604080516323b95ceb60e21b81526001600160a01b0389811660048301529151929350600092918b1691638ee573ac91602480820192602092909190829003018186803b15801561464657600080fd5b505afa15801561465a573d6000803e3d6000fd5b505050506040513d602081101561467057600080fd5b5051905060008061469168327cb2734119d3b7a9601e1b611f678a886157bc565b90506146ad600a85900a611f6783670de0b6b3a76400006157bc565b905060008b6001600160a01b03166342b60b038c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156146fe57600080fd5b505afa158015614712573d6000803e3d6000fd5b505050506040513d602081101561472857600080fd5b505180156147ab57508b6001600160a01b03166342b60b038b6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561477e57600080fd5b505afa158015614792573d6000803e3d6000fd5b505050506040513d60208110156147a857600080fd5b50515b905060008161481e578c6001600160a01b031663a22f23926040518163ffffffff1660e01b815260040160206040518083038186803b1580156147ed57600080fd5b505afa158015614801573d6000803e3d6000fd5b505050506040513d602081101561481757600080fd5b5051614884565b8c6001600160a01b031663df73a2676040518163ffffffff1660e01b815260040160206040518083038186803b15801561485757600080fd5b505afa15801561486b573d6000803e3d6000fd5b505050506040513d602081101561488157600080fd5b50515b90506000826148f7578d6001600160a01b0316637a210a2b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156148c657600080fd5b505afa1580156148da573d6000803e3d6000fd5b505050506040513d60208110156148f057600080fd5b505161495d565b8d6001600160a01b03166310eb56c26040518163ffffffff1660e01b815260040160206040518083038186803b15801561493057600080fd5b505afa158015614944573d6000803e3d6000fd5b505050506040513d602081101561495a57600080fd5b50515b905060008e6001600160a01b031663c7e074c38f87868660016040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b1580156149d157600080fd5b505afa1580156149e5573d6000803e3d6000fd5b505050506040513d60208110156149fb57600080fd5b8101908080519060200190929190505050905060008f6001600160a01b031663c7e074c38f88878760006040518663ffffffff1660e01b815260040180866001600160a01b0316815260200185815260200184815260200183815260200182151581526020019550505050505060206040518083038186803b158015614a8057600080fd5b505afa158015614a94573d6000803e3d6000fd5b505050506040513d6020811015614aaa57600080fd5b50519050808211614abb5780614abd565b815b965050505050505060008a6001600160a01b031663e124e6d28a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015614b1457600080fd5b505afa158015614b28573d6000803e3d6000fd5b505050506040513d6020811015614b3e57600080fd5b505190506000614b5282611f678b896157bc565b9050614b7185600a0a611f6786600a0a846157bc90919063ffffffff16565b90506000614b8f612710611f67614b88828861577a565b85906157bc565b90506000614b9d838361577a565b919e919d50909b505050505050505050505050565b60608082516001600160401b0381118015614bcc57600080fd5b50604051908082528060200260200182016040528015614bf6578160200160208202803683370190505b50905060005b8351811015611805576000848281518110614c1357fe5b6020026020010151905060006001600160a01b0316816001600160a01b03161415614c6157856001600160a01b031631838381518110614c4f57fe5b60200260200101818152505050614cf6565b806001600160a01b03166370a08231876040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015614cae57600080fd5b505afa158015614cc2573d6000803e3d6000fd5b505050506040513d6020811015614cd857600080fd5b50518351849084908110614ce857fe5b602002602001018181525050505b600101614bfc565b60608060098551026001600160401b0381118015614d1b57600080fd5b50604051908082528060200260200182016040528015614d45578160200160208202803683370190505b50905060005b85518110156150ef5760008060008060008060008e6001600160a01b0316634a3f088d8f8f8b81518110614d7b57fe5b60200260200101518f8c81518110614d8f57fe5b60200260200101518f8d81518110614da357fe5b60200260200101516040518563ffffffff1660e01b815260040180856001600160a01b03168152602001846001600160a01b03168152602001836001600160a01b0316815260200182151581526020019450505050506101006040518083038186803b158015614e1257600080fd5b505afa158015614e26573d6000803e3d6000fd5b505050506040513d610100811015614e3d57600080fd5b50805160208201516040830151606084015160a085015160c086015160e0909601518f51959d50939b509199509750955091935090915087908a9060098b02908110614e8557fe5b602002602001018181525050858960098a0260010181518110614ea457fe5b602002602001018181525050848960098a0260020181518110614ec357fe5b602002602001018181525050838960098a0260030181518110614ee257fe5b60200260200101818152505081614efa576000614efd565b60015b60ff168960098a0260040181518110614f1257fe5b602002602001018181525050828960098a0260050181518110614f3157fe5b602002602001018181525050808960098a0260060181518110614f5057fe5b602002602001018181525050505050505050506000826009830281518110614f7457fe5b602002602001015190506000836009840260020181518110614f9257fe5b602002602001015190506000846009850260060181518110614fb057fe5b6020026020010151905060008211156150e4576000808c6001600160a01b0316635c07eaab8b8881518110614fe157fe5b602002602001015187878d8b81518110614ff757fe5b6020026020010151886040518663ffffffff1660e01b815260040180866001600160a01b03168152602001858152602001848152602001831515815260200182815260200195505050505050604080518083038186803b15801561505a57600080fd5b505afa15801561506e573d6000803e3d6000fd5b505050506040513d604081101561508457600080fd5b50805160209091015190925090508161509e5760006150a1565b60015b60ff168760098802600701815181106150b657fe5b602002602001018181525050808760098802600801815181106150d557fe5b60200260200101818152505050505b505050600101614d4b565b509695505050505050565b600080846001600160a01b03166381a612d6856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561514a57600080fd5b505afa15801561515e573d6000803e3d6000fd5b505050506040513d602081101561517457600080fd5b505160408051637092736960e11b81526001600160a01b03868116600483015291519293506000929188169163e124e6d291602480820192602092909190829003018186803b1580156151c657600080fd5b505afa1580156151da573d6000803e3d6000fd5b505050506040513d60208110156151f057600080fd5b5051604080516323b95ceb60e21b81526001600160a01b038881166004830152915192935060009291891691638ee573ac91602480820192602092909190829003018186803b15801561524257600080fd5b505afa158015615256573d6000803e3d6000fd5b505050506040513d602081101561526c57600080fd5b5051604080516323b95ceb60e21b81526001600160a01b0388811660048301529151929350600092918a1691638ee573ac91602480820192602092909190829003018186803b1580156152be57600080fd5b505afa1580156152d2573d6000803e3d6000fd5b505050506040513d60208110156152e857600080fd5b5051604080516352f55eed60e01b81526001600160a01b038981166004830152915192935060009283928c16916352f55eed916024808301926020929190829003018186803b15801561533a57600080fd5b505afa15801561534e573d6000803e3d6000fd5b505050506040513d602081101561536457600080fd5b50516040805163c3c7b9e960e01b81526001600160a01b038b811660048301529151929350600092918d169163c3c7b9e991602480820192602092909190829003018186803b1580156153b657600080fd5b505afa1580156153ca573d6000803e3d6000fd5b505050506040513d60208110156153e057600080fd5b505160408051634a993ee960e01b81526001600160a01b038c811660048301529151929350600092918e1691634a993ee991602480820192602092909190829003018186803b15801561543257600080fd5b505afa158015615446573d6000803e3d6000fd5b505050506040513d602081101561545c57600080fd5b50519050600081831161546f5781615471565b825b905083811061548c5760009950505050505050505050614186565b6000615498858361577a565b90506154c587600a0a611f678a600a0a6154bf8e611f678f886157bc90919063ffffffff16565b906157bc565b955050505050506000896001600160a01b031663ad1e4f8d8a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561551b57600080fd5b505afa15801561552f573d6000803e3d6000fd5b505050506040513d602081101561554557600080fd5b5051905080156115de57896001600160a01b0316631aa4ace58a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561559c57600080fd5b505afa1580156155b0573d6000803e3d6000fd5b505050506040513d60208110156155c657600080fd5b50518110156155de5760009650505050505050614186565b60006156638b6001600160a01b0316631aa4ace58c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561563057600080fd5b505afa158015615644573d6000803e3d6000fd5b505050506040513d602081101561565a57600080fd5b5051839061577a565b905061567f670de0b6b3a7640000611f6783600a89900a6157bc565b905061569b87611f678368327cb2734119d3b7a9601e1b6157bc565b9050808311156156b357965061418695505050505050565b505098975050505050505050565b600080805b83518110156118055760008482815181106156dd57fe5b602002602001015190506000866001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561573657600080fd5b505afa15801561574a573d6000803e3d6000fd5b505050506040513d602081101561576057600080fd5b5051905061576e8482615854565b935050506001016156c6565b600061418683836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506158ac565b6000826157cb57506000611809565b828202828482816157d857fe5b04146141865760405162461bcd60e51b81526004018080602001828103825260218152602001806159a96021913960400191505060405180910390fd5b600061418683836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250615943565b600082820183811015614186576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6000818484111561593b5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156159005781810151838201526020016158e8565b50505050905090810190601f16801561592d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600081836159925760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156159005781810151838201526020016158e8565b50600083858161599e57fe5b049594505050505056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122045b628c599a8154fc42d4309c34d7a311429d723c48990eab8da614a960534d364736f6c634300060c0033"; + public ReaderDeploymentBase() : base(BYTECODE) { } + public ReaderDeploymentBase(string byteCode) : base(byteCode) { } + + } + + public partial class BasisPointsDivisorFunction : BasisPointsDivisorFunctionBase { } + + [Function("BASIS_POINTS_DIVISOR", "uint256")] + public class BasisPointsDivisorFunctionBase : FunctionMessage + { + + } + + public partial class PositionPropsLengthFunction : PositionPropsLengthFunctionBase { } + + [Function("POSITION_PROPS_LENGTH", "uint256")] + public class PositionPropsLengthFunctionBase : FunctionMessage + { + + } + + public partial class PricePrecisionFunction : PricePrecisionFunctionBase { } + + [Function("PRICE_PRECISION", "uint256")] + public class PricePrecisionFunctionBase : FunctionMessage + { + + } + + public partial class UsdgDecimalsFunction : UsdgDecimalsFunctionBase { } + + [Function("USDG_DECIMALS", "uint256")] + public class UsdgDecimalsFunctionBase : FunctionMessage + { + + } + + public partial class GetAmountOutFunction : GetAmountOutFunctionBase { } + + [Function("getAmountOut", typeof(GetAmountOutOutputDTO))] + public class GetAmountOutFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_tokenIn", 2)] + public virtual string TokenIn { get; set; } + [Parameter("address", "_tokenOut", 3)] + public virtual string TokenOut { get; set; } + [Parameter("uint256", "_amountIn", 4)] + public virtual BigInteger AmountIn { get; set; } + } + + public partial class GetFeeBasisPointsFunction : GetFeeBasisPointsFunctionBase { } + + [Function("getFeeBasisPoints", typeof(GetFeeBasisPointsOutputDTO))] + public class GetFeeBasisPointsFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_tokenIn", 2)] + public virtual string TokenIn { get; set; } + [Parameter("address", "_tokenOut", 3)] + public virtual string TokenOut { get; set; } + [Parameter("uint256", "_amountIn", 4)] + public virtual BigInteger AmountIn { get; set; } + } + + public partial class GetFeesFunction : GetFeesFunctionBase { } + + [Function("getFees", "uint256[]")] + public class GetFeesFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address[]", "_tokens", 2)] + public virtual List Tokens { get; set; } + } + + public partial class GetFullVaultTokenInfoFunction : GetFullVaultTokenInfoFunctionBase { } + + [Function("getFullVaultTokenInfo", "uint256[]")] + public class GetFullVaultTokenInfoFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_weth", 2)] + public virtual string Weth { get; set; } + [Parameter("uint256", "_usdgAmount", 3)] + public virtual BigInteger UsdgAmount { get; set; } + [Parameter("address[]", "_tokens", 4)] + public virtual List Tokens { get; set; } + } + + public partial class GetFundingRatesFunction : GetFundingRatesFunctionBase { } + + [Function("getFundingRates", "uint256[]")] + public class GetFundingRatesFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_weth", 2)] + public virtual string Weth { get; set; } + [Parameter("address[]", "_tokens", 3)] + public virtual List Tokens { get; set; } + } + + public partial class GetMaxAmountInFunction : GetMaxAmountInFunctionBase { } + + [Function("getMaxAmountIn", "uint256")] + public class GetMaxAmountInFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_tokenIn", 2)] + public virtual string TokenIn { get; set; } + [Parameter("address", "_tokenOut", 3)] + public virtual string TokenOut { get; set; } + } + + public partial class GetPairInfoFunction : GetPairInfoFunctionBase { } + + [Function("getPairInfo", "uint256[]")] + public class GetPairInfoFunctionBase : FunctionMessage + { + [Parameter("address", "_factory", 1)] + public virtual string Factory { get; set; } + [Parameter("address[]", "_tokens", 2)] + public virtual List Tokens { get; set; } + } + + public partial class GetPositionsFunction : GetPositionsFunctionBase { } + + [Function("getPositions", "uint256[]")] + public class GetPositionsFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_account", 2)] + public virtual string Account { get; set; } + [Parameter("address[]", "_collateralTokens", 3)] + public virtual List CollateralTokens { get; set; } + [Parameter("address[]", "_indexTokens", 4)] + public virtual List IndexTokens { get; set; } + [Parameter("bool[]", "_isLong", 5)] + public virtual List IsLong { get; set; } + } + + public partial class GetPricesFunction : GetPricesFunctionBase { } + + [Function("getPrices", "uint256[]")] + public class GetPricesFunctionBase : FunctionMessage + { + [Parameter("address", "_priceFeed", 1)] + public virtual string PriceFeed { get; set; } + [Parameter("address[]", "_tokens", 2)] + public virtual List Tokens { get; set; } + } + + public partial class GetStakingInfoFunction : GetStakingInfoFunctionBase { } + + [Function("getStakingInfo", "uint256[]")] + public class GetStakingInfoFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("address[]", "_yieldTrackers", 2)] + public virtual List YieldTrackers { get; set; } + } + + public partial class GetTokenBalancesFunction : GetTokenBalancesFunctionBase { } + + [Function("getTokenBalances", "uint256[]")] + public class GetTokenBalancesFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("address[]", "_tokens", 2)] + public virtual List Tokens { get; set; } + } + + public partial class GetTokenBalancesWithSuppliesFunction : GetTokenBalancesWithSuppliesFunctionBase { } + + [Function("getTokenBalancesWithSupplies", "uint256[]")] + public class GetTokenBalancesWithSuppliesFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("address[]", "_tokens", 2)] + public virtual List Tokens { get; set; } + } + + public partial class GetTokenSupplyFunction : GetTokenSupplyFunctionBase { } + + [Function("getTokenSupply", "uint256")] + public class GetTokenSupplyFunctionBase : FunctionMessage + { + [Parameter("address", "_token", 1)] + public virtual string Token { get; set; } + [Parameter("address[]", "_excludedAccounts", 2)] + public virtual List ExcludedAccounts { get; set; } + } + + public partial class GetTotalBalanceFunction : GetTotalBalanceFunctionBase { } + + [Function("getTotalBalance", "uint256")] + public class GetTotalBalanceFunctionBase : FunctionMessage + { + [Parameter("address", "_token", 1)] + public virtual string Token { get; set; } + [Parameter("address[]", "_accounts", 2)] + public virtual List Accounts { get; set; } + } + + public partial class GetTotalStakedFunction : GetTotalStakedFunctionBase { } + + [Function("getTotalStaked", "uint256[]")] + public class GetTotalStakedFunctionBase : FunctionMessage + { + [Parameter("address[]", "_yieldTokens", 1)] + public virtual List YieldTokens { get; set; } + } + + public partial class GetVaultTokenInfoFunction : GetVaultTokenInfoFunctionBase { } + + [Function("getVaultTokenInfo", "uint256[]")] + public class GetVaultTokenInfoFunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_weth", 2)] + public virtual string Weth { get; set; } + [Parameter("uint256", "_usdgAmount", 3)] + public virtual BigInteger UsdgAmount { get; set; } + [Parameter("address[]", "_tokens", 4)] + public virtual List Tokens { get; set; } + } + + public partial class GetVaultTokenInfoV2Function : GetVaultTokenInfoV2FunctionBase { } + + [Function("getVaultTokenInfoV2", "uint256[]")] + public class GetVaultTokenInfoV2FunctionBase : FunctionMessage + { + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_weth", 2)] + public virtual string Weth { get; set; } + [Parameter("uint256", "_usdgAmount", 3)] + public virtual BigInteger UsdgAmount { get; set; } + [Parameter("address[]", "_tokens", 4)] + public virtual List Tokens { get; set; } + } + + public partial class GetVestingInfoFunction : GetVestingInfoFunctionBase { } + + [Function("getVestingInfo", "uint256[]")] + public class GetVestingInfoFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("address[]", "_vesters", 2)] + public virtual List Vesters { get; set; } + } + + public partial class GovFunction : GovFunctionBase { } + + [Function("gov", "address")] + public class GovFunctionBase : FunctionMessage + { + + } + + public partial class HasMaxGlobalShortSizesFunction : HasMaxGlobalShortSizesFunctionBase { } + + [Function("hasMaxGlobalShortSizes", "bool")] + public class HasMaxGlobalShortSizesFunctionBase : FunctionMessage + { + + } + + public partial class SetConfigFunction : SetConfigFunctionBase { } + + [Function("setConfig")] + public class SetConfigFunctionBase : FunctionMessage + { + [Parameter("bool", "_hasMaxGlobalShortSizes", 1)] + public virtual bool HasMaxGlobalShortSizes { get; set; } + } + + public partial class SetGovFunction : SetGovFunctionBase { } + + [Function("setGov")] + public class SetGovFunctionBase : FunctionMessage + { + [Parameter("address", "_gov", 1)] + public virtual string Gov { get; set; } + } + + public partial class BasisPointsDivisorOutputDTO : BasisPointsDivisorOutputDTOBase { } + + [FunctionOutput] + public class BasisPointsDivisorOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class PositionPropsLengthOutputDTO : PositionPropsLengthOutputDTOBase { } + + [FunctionOutput] + public class PositionPropsLengthOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class PricePrecisionOutputDTO : PricePrecisionOutputDTOBase { } + + [FunctionOutput] + public class PricePrecisionOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class UsdgDecimalsOutputDTO : UsdgDecimalsOutputDTOBase { } + + [FunctionOutput] + public class UsdgDecimalsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class GetAmountOutOutputDTO : GetAmountOutOutputDTOBase { } + + [FunctionOutput] + public class GetAmountOutOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + [Parameter("uint256", "", 2)] + public virtual BigInteger ReturnValue2 { get; set; } + } + + public partial class GetFeeBasisPointsOutputDTO : GetFeeBasisPointsOutputDTOBase { } + + [FunctionOutput] + public class GetFeeBasisPointsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + [Parameter("uint256", "", 2)] + public virtual BigInteger ReturnValue2 { get; set; } + [Parameter("uint256", "", 3)] + public virtual BigInteger ReturnValue3 { get; set; } + } + + public partial class GetFeesOutputDTO : GetFeesOutputDTOBase { } + + [FunctionOutput] + public class GetFeesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetFullVaultTokenInfoOutputDTO : GetFullVaultTokenInfoOutputDTOBase { } + + [FunctionOutput] + public class GetFullVaultTokenInfoOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetFundingRatesOutputDTO : GetFundingRatesOutputDTOBase { } + + [FunctionOutput] + public class GetFundingRatesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetMaxAmountInOutputDTO : GetMaxAmountInOutputDTOBase { } + + [FunctionOutput] + public class GetMaxAmountInOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class GetPairInfoOutputDTO : GetPairInfoOutputDTOBase { } + + [FunctionOutput] + public class GetPairInfoOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetPositionsOutputDTO : GetPositionsOutputDTOBase { } + + [FunctionOutput] + public class GetPositionsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetPricesOutputDTO : GetPricesOutputDTOBase { } + + [FunctionOutput] + public class GetPricesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetStakingInfoOutputDTO : GetStakingInfoOutputDTOBase { } + + [FunctionOutput] + public class GetStakingInfoOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetTokenBalancesOutputDTO : GetTokenBalancesOutputDTOBase { } + + [FunctionOutput] + public class GetTokenBalancesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetTokenBalancesWithSuppliesOutputDTO : GetTokenBalancesWithSuppliesOutputDTOBase { } + + [FunctionOutput] + public class GetTokenBalancesWithSuppliesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetTokenSupplyOutputDTO : GetTokenSupplyOutputDTOBase { } + + [FunctionOutput] + public class GetTokenSupplyOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class GetTotalBalanceOutputDTO : GetTotalBalanceOutputDTOBase { } + + [FunctionOutput] + public class GetTotalBalanceOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class GetTotalStakedOutputDTO : GetTotalStakedOutputDTOBase { } + + [FunctionOutput] + public class GetTotalStakedOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetVaultTokenInfoOutputDTO : GetVaultTokenInfoOutputDTOBase { } + + [FunctionOutput] + public class GetVaultTokenInfoOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetVaultTokenInfoV2OutputDTO : GetVaultTokenInfoV2OutputDTOBase { } + + [FunctionOutput] + public class GetVaultTokenInfoV2OutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GetVestingInfoOutputDTO : GetVestingInfoOutputDTOBase { } + + [FunctionOutput] + public class GetVestingInfoOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256[]", "", 1)] + public virtual List ReturnValue1 { get; set; } + } + + public partial class GovOutputDTO : GovOutputDTOBase { } + + [FunctionOutput] + public class GovOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class HasMaxGlobalShortSizesOutputDTO : HasMaxGlobalShortSizesOutputDTOBase { } + + [FunctionOutput] + public class HasMaxGlobalShortSizesOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bool", "", 1)] + public virtual bool ReturnValue1 { get; set; } + } + + + + +} diff --git a/src/Managing.Tools.ABI/Reader/ReaderService.cs b/src/Managing.Tools.ABI/Reader/ReaderService.cs new file mode 100644 index 0000000..9b0349f --- /dev/null +++ b/src/Managing.Tools.ABI/Reader/ReaderService.cs @@ -0,0 +1,443 @@ +using System.Numerics; +using Nethereum.Web3; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.Contracts.ContractHandlers; +using Managing.Tools.Reader.ContractDefinition; + +namespace Managing.Tools.Reader +{ + public partial class ReaderService + { + public static Task DeployContractAndWaitForReceiptAsync(Web3 web3, ReaderDeployment readerDeployment, CancellationTokenSource cancellationTokenSource = null) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAndWaitForReceiptAsync(readerDeployment, cancellationTokenSource); + } + + public static Task DeployContractAsync(Web3 web3, ReaderDeployment readerDeployment) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAsync(readerDeployment); + } + + public static async Task DeployContractAndGetServiceAsync(Web3 web3, ReaderDeployment readerDeployment, CancellationTokenSource cancellationTokenSource = null) + { + var receipt = await DeployContractAndWaitForReceiptAsync(web3, readerDeployment, cancellationTokenSource); + return new ReaderService(web3, receipt.ContractAddress); + } + + protected IWeb3 Web3 { get; } + + public ContractHandler ContractHandler { get; } + + public ReaderService(Web3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public ReaderService(IWeb3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public Task BasisPointsDivisorQueryAsync(BasisPointsDivisorFunction basisPointsDivisorFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(basisPointsDivisorFunction, blockParameter); + } + + + public Task BasisPointsDivisorQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task PositionPropsLengthQueryAsync(PositionPropsLengthFunction positionPropsLengthFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(positionPropsLengthFunction, blockParameter); + } + + + public Task PositionPropsLengthQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task PricePrecisionQueryAsync(PricePrecisionFunction pricePrecisionFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(pricePrecisionFunction, blockParameter); + } + + + public Task PricePrecisionQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task UsdgDecimalsQueryAsync(UsdgDecimalsFunction usdgDecimalsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(usdgDecimalsFunction, blockParameter); + } + + + public Task UsdgDecimalsQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task GetAmountOutQueryAsync(GetAmountOutFunction getAmountOutFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getAmountOutFunction, blockParameter); + } + + public Task GetAmountOutQueryAsync(string vault, string tokenIn, string tokenOut, BigInteger amountIn, BlockParameter blockParameter = null) + { + var getAmountOutFunction = new GetAmountOutFunction(); + getAmountOutFunction.Vault = vault; + getAmountOutFunction.TokenIn = tokenIn; + getAmountOutFunction.TokenOut = tokenOut; + getAmountOutFunction.AmountIn = amountIn; + + return ContractHandler.QueryDeserializingToObjectAsync(getAmountOutFunction, blockParameter); + } + + public Task GetFeeBasisPointsQueryAsync(GetFeeBasisPointsFunction getFeeBasisPointsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryDeserializingToObjectAsync(getFeeBasisPointsFunction, blockParameter); + } + + public Task GetFeeBasisPointsQueryAsync(string vault, string tokenIn, string tokenOut, BigInteger amountIn, BlockParameter blockParameter = null) + { + var getFeeBasisPointsFunction = new GetFeeBasisPointsFunction(); + getFeeBasisPointsFunction.Vault = vault; + getFeeBasisPointsFunction.TokenIn = tokenIn; + getFeeBasisPointsFunction.TokenOut = tokenOut; + getFeeBasisPointsFunction.AmountIn = amountIn; + + return ContractHandler.QueryDeserializingToObjectAsync(getFeeBasisPointsFunction, blockParameter); + } + + public Task> GetFeesQueryAsync(GetFeesFunction getFeesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getFeesFunction, blockParameter); + } + + + public Task> GetFeesQueryAsync(string vault, List tokens, BlockParameter blockParameter = null) + { + var getFeesFunction = new GetFeesFunction(); + getFeesFunction.Vault = vault; + getFeesFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getFeesFunction, blockParameter); + } + + public Task> GetFullVaultTokenInfoQueryAsync(GetFullVaultTokenInfoFunction getFullVaultTokenInfoFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getFullVaultTokenInfoFunction, blockParameter); + } + + + public Task> GetFullVaultTokenInfoQueryAsync(string vault, string weth, BigInteger usdgAmount, List tokens, BlockParameter blockParameter = null) + { + var getFullVaultTokenInfoFunction = new GetFullVaultTokenInfoFunction(); + getFullVaultTokenInfoFunction.Vault = vault; + getFullVaultTokenInfoFunction.Weth = weth; + getFullVaultTokenInfoFunction.UsdgAmount = usdgAmount; + getFullVaultTokenInfoFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getFullVaultTokenInfoFunction, blockParameter); + } + + public Task> GetFundingRatesQueryAsync(GetFundingRatesFunction getFundingRatesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getFundingRatesFunction, blockParameter); + } + + + public Task> GetFundingRatesQueryAsync(string vault, string weth, List tokens, BlockParameter blockParameter = null) + { + var getFundingRatesFunction = new GetFundingRatesFunction(); + getFundingRatesFunction.Vault = vault; + getFundingRatesFunction.Weth = weth; + getFundingRatesFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getFundingRatesFunction, blockParameter); + } + + public Task GetMaxAmountInQueryAsync(GetMaxAmountInFunction getMaxAmountInFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(getMaxAmountInFunction, blockParameter); + } + + + public Task GetMaxAmountInQueryAsync(string vault, string tokenIn, string tokenOut, BlockParameter blockParameter = null) + { + var getMaxAmountInFunction = new GetMaxAmountInFunction(); + getMaxAmountInFunction.Vault = vault; + getMaxAmountInFunction.TokenIn = tokenIn; + getMaxAmountInFunction.TokenOut = tokenOut; + + return ContractHandler.QueryAsync(getMaxAmountInFunction, blockParameter); + } + + public Task> GetPairInfoQueryAsync(GetPairInfoFunction getPairInfoFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getPairInfoFunction, blockParameter); + } + + + public Task> GetPairInfoQueryAsync(string factory, List tokens, BlockParameter blockParameter = null) + { + var getPairInfoFunction = new GetPairInfoFunction(); + getPairInfoFunction.Factory = factory; + getPairInfoFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getPairInfoFunction, blockParameter); + } + + public Task> GetPositionsQueryAsync(GetPositionsFunction getPositionsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getPositionsFunction, blockParameter); + } + + + public Task> GetPositionsQueryAsync(string vault, string account, List collateralTokens, List indexTokens, List isLong, BlockParameter blockParameter = null) + { + var getPositionsFunction = new GetPositionsFunction(); + getPositionsFunction.Vault = vault; + getPositionsFunction.Account = account; + getPositionsFunction.CollateralTokens = collateralTokens; + getPositionsFunction.IndexTokens = indexTokens; + getPositionsFunction.IsLong = isLong; + + return ContractHandler.QueryAsync>(getPositionsFunction, blockParameter); + } + + public Task> GetPricesQueryAsync(GetPricesFunction getPricesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getPricesFunction, blockParameter); + } + + + public Task> GetPricesQueryAsync(string priceFeed, List tokens, BlockParameter blockParameter = null) + { + var getPricesFunction = new GetPricesFunction(); + getPricesFunction.PriceFeed = priceFeed; + getPricesFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getPricesFunction, blockParameter); + } + + public Task> GetStakingInfoQueryAsync(GetStakingInfoFunction getStakingInfoFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getStakingInfoFunction, blockParameter); + } + + + public Task> GetStakingInfoQueryAsync(string account, List yieldTrackers, BlockParameter blockParameter = null) + { + var getStakingInfoFunction = new GetStakingInfoFunction(); + getStakingInfoFunction.Account = account; + getStakingInfoFunction.YieldTrackers = yieldTrackers; + + return ContractHandler.QueryAsync>(getStakingInfoFunction, blockParameter); + } + + public Task> GetTokenBalancesQueryAsync(GetTokenBalancesFunction getTokenBalancesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getTokenBalancesFunction, blockParameter); + } + + + public Task> GetTokenBalancesQueryAsync(string account, List tokens, BlockParameter blockParameter = null) + { + var getTokenBalancesFunction = new GetTokenBalancesFunction(); + getTokenBalancesFunction.Account = account; + getTokenBalancesFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getTokenBalancesFunction, blockParameter); + } + + public Task> GetTokenBalancesWithSuppliesQueryAsync(GetTokenBalancesWithSuppliesFunction getTokenBalancesWithSuppliesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getTokenBalancesWithSuppliesFunction, blockParameter); + } + + + public Task> GetTokenBalancesWithSuppliesQueryAsync(string account, List tokens, BlockParameter blockParameter = null) + { + var getTokenBalancesWithSuppliesFunction = new GetTokenBalancesWithSuppliesFunction(); + getTokenBalancesWithSuppliesFunction.Account = account; + getTokenBalancesWithSuppliesFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getTokenBalancesWithSuppliesFunction, blockParameter); + } + + public Task GetTokenSupplyQueryAsync(GetTokenSupplyFunction getTokenSupplyFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(getTokenSupplyFunction, blockParameter); + } + + + public Task GetTokenSupplyQueryAsync(string token, List excludedAccounts, BlockParameter blockParameter = null) + { + var getTokenSupplyFunction = new GetTokenSupplyFunction(); + getTokenSupplyFunction.Token = token; + getTokenSupplyFunction.ExcludedAccounts = excludedAccounts; + + return ContractHandler.QueryAsync(getTokenSupplyFunction, blockParameter); + } + + public Task GetTotalBalanceQueryAsync(GetTotalBalanceFunction getTotalBalanceFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(getTotalBalanceFunction, blockParameter); + } + + + public Task GetTotalBalanceQueryAsync(string token, List accounts, BlockParameter blockParameter = null) + { + var getTotalBalanceFunction = new GetTotalBalanceFunction(); + getTotalBalanceFunction.Token = token; + getTotalBalanceFunction.Accounts = accounts; + + return ContractHandler.QueryAsync(getTotalBalanceFunction, blockParameter); + } + + public Task> GetTotalStakedQueryAsync(GetTotalStakedFunction getTotalStakedFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getTotalStakedFunction, blockParameter); + } + + + public Task> GetTotalStakedQueryAsync(List yieldTokens, BlockParameter blockParameter = null) + { + var getTotalStakedFunction = new GetTotalStakedFunction(); + getTotalStakedFunction.YieldTokens = yieldTokens; + + return ContractHandler.QueryAsync>(getTotalStakedFunction, blockParameter); + } + + public Task> GetVaultTokenInfoQueryAsync(GetVaultTokenInfoFunction getVaultTokenInfoFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getVaultTokenInfoFunction, blockParameter); + } + + + public Task> GetVaultTokenInfoQueryAsync(string vault, string weth, BigInteger usdgAmount, List tokens, BlockParameter blockParameter = null) + { + var getVaultTokenInfoFunction = new GetVaultTokenInfoFunction(); + getVaultTokenInfoFunction.Vault = vault; + getVaultTokenInfoFunction.Weth = weth; + getVaultTokenInfoFunction.UsdgAmount = usdgAmount; + getVaultTokenInfoFunction.Tokens = tokens; + + return ContractHandler.QueryAsync>(getVaultTokenInfoFunction, blockParameter); + } + + public Task> GetVaultTokenInfoV2QueryAsync(GetVaultTokenInfoV2Function getVaultTokenInfoV2Function, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getVaultTokenInfoV2Function, blockParameter); + } + + + public Task> GetVaultTokenInfoV2QueryAsync(string vault, string weth, BigInteger usdgAmount, List tokens, BlockParameter blockParameter = null) + { + var getVaultTokenInfoV2Function = new GetVaultTokenInfoV2Function(); + getVaultTokenInfoV2Function.Vault = vault; + getVaultTokenInfoV2Function.Weth = weth; + getVaultTokenInfoV2Function.UsdgAmount = usdgAmount; + getVaultTokenInfoV2Function.Tokens = tokens; + + return ContractHandler.QueryAsync>(getVaultTokenInfoV2Function, blockParameter); + } + + public Task> GetVestingInfoQueryAsync(GetVestingInfoFunction getVestingInfoFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync>(getVestingInfoFunction, blockParameter); + } + + + public Task> GetVestingInfoQueryAsync(string account, List vesters, BlockParameter blockParameter = null) + { + var getVestingInfoFunction = new GetVestingInfoFunction(); + getVestingInfoFunction.Account = account; + getVestingInfoFunction.Vesters = vesters; + + return ContractHandler.QueryAsync>(getVestingInfoFunction, blockParameter); + } + + public Task GovQueryAsync(GovFunction govFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(govFunction, blockParameter); + } + + + public Task GovQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task HasMaxGlobalShortSizesQueryAsync(HasMaxGlobalShortSizesFunction hasMaxGlobalShortSizesFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(hasMaxGlobalShortSizesFunction, blockParameter); + } + + + public Task HasMaxGlobalShortSizesQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task SetConfigRequestAsync(SetConfigFunction setConfigFunction) + { + return ContractHandler.SendRequestAsync(setConfigFunction); + } + + public Task SetConfigRequestAndWaitForReceiptAsync(SetConfigFunction setConfigFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setConfigFunction, cancellationToken); + } + + public Task SetConfigRequestAsync(bool hasMaxGlobalShortSizes) + { + var setConfigFunction = new SetConfigFunction(); + setConfigFunction.HasMaxGlobalShortSizes = hasMaxGlobalShortSizes; + + return ContractHandler.SendRequestAsync(setConfigFunction); + } + + public Task SetConfigRequestAndWaitForReceiptAsync(bool hasMaxGlobalShortSizes, CancellationTokenSource cancellationToken = null) + { + var setConfigFunction = new SetConfigFunction(); + setConfigFunction.HasMaxGlobalShortSizes = hasMaxGlobalShortSizes; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setConfigFunction, cancellationToken); + } + + public Task SetGovRequestAsync(SetGovFunction setGovFunction) + { + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(SetGovFunction setGovFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + + public Task SetGovRequestAsync(string gov) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(string gov, CancellationTokenSource cancellationToken = null) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + } +} diff --git a/src/Managing.Tools.ABI/Router/ContractDefinition/RouterDefinition.cs b/src/Managing.Tools.ABI/Router/ContractDefinition/RouterDefinition.cs new file mode 100644 index 0000000..eee8d03 --- /dev/null +++ b/src/Managing.Tools.ABI/Router/ContractDefinition/RouterDefinition.cs @@ -0,0 +1,466 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +namespace Managing.Tools.Router.ContractDefinition +{ + + + public partial class RouterDeployment : RouterDeploymentBase + { + public RouterDeployment() : base(BYTECODE) { } + public RouterDeployment(string byteCode) : base(byteCode) { } + } + + public class RouterDeploymentBase : ContractDeploymentMessage + { + public static string BYTECODE = "608060405234801561001057600080fd5b506040516124773803806124778339818101604052606081101561003357600080fd5b5080516020820151604090920151600380546001600160a01b039384166001600160a01b0319918216179091556002805494841694821694909417909355600180549290911691831691909117905560008054909116331790556123db8061009c6000396000f3fe60806040526004361061011f5760003560e01c806312d43a51146101835780631b827878146101b45780631f1dd176146101fd5780632662166b1461024e5780632d4ba6a7146102c15780633039e37f1461038357806338c74dd9146104635780633fc8cef314610496578063430ed37c146104ab5780634b12e6431461050a5780635fc8500e146105515780636023e9661461063157806390205d8c146106f357806390b64ad314610752578063956f285e1461078b578063a4d95b64146107c6578063abe68eaa146107f9578063b32755de146108a8578063b7ddc9921461096b578063cedd437514610a41578063cfad57a214610a74578063d8867fc814610aa7578063f5b91b7b14610ada578063fbfa77cf14610aef5761017e565b3661017e576001546001600160a01b0316331461017c576040805162461bcd60e51b81526020600482015260166024820152752937baba32b91d1034b73b30b634b21039b2b73232b960511b604482015290519081900360640190fd5b005b600080fd5b34801561018f57600080fd5b50610198610b04565b604080516001600160a01b039092168252519081900360200190f35b3480156101c057600080fd5b5061017c600480360360808110156101d757600080fd5b506001600160a01b03813581169160208101358216916040820135169060600135610b13565b34801561020957600080fd5b5061017c600480360360a081101561022057600080fd5b506001600160a01b038135811691602081013582169160408201351690606081013590608001351515610b37565b34801561025a57600080fd5b506102af600480360360e081101561027157600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359160808201359160a081013515159160c09091013516610bcc565b60408051918252519081900360200190f35b3480156102cd57600080fd5b5061017c600480360360808110156102e457600080fd5b810190602081018135600160201b8111156102fe57600080fd5b82018360208201111561031057600080fd5b803590602001918460208302840111600160201b8311171561033157600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602081013590604001356001600160a01b0316610c8c565b34801561038f57600080fd5b5061017c60048036036101008110156103a757600080fd5b810190602081018135600160201b8111156103c157600080fd5b8201836020820111156103d357600080fd5b803590602001918460208302840111600160201b831117156103f457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b0390811693506020830135926040810135925060608101351515916080820135169060a08101359060c00135610df3565b34801561046f57600080fd5b5061017c6004803603602081101561048657600080fd5b50356001600160a01b0316610ef8565b3480156104a257600080fd5b50610198610f29565b3480156104b757600080fd5b5061017c600480360360e08110156104ce57600080fd5b506001600160a01b0381358116916020810135821691604082013591606081013591608082013515159160a08101359091169060c00135610f38565b34801561051657600080fd5b5061053d6004803603602081101561052d57600080fd5b50356001600160a01b0316610f5f565b604080519115158252519081900360200190f35b34801561055d57600080fd5b5061017c600480360361010081101561057557600080fd5b810190602081018135600160201b81111561058f57600080fd5b8201836020820111156105a157600080fd5b803590602001918460208302840111600160201b831117156105c257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505081356001600160a01b0390811693506020830135926040810135925060608101351515916080820135169060a08101359060c00135610f74565b34801561063d57600080fd5b5061017c6004803603608081101561065457600080fd5b810190602081018135600160201b81111561066e57600080fd5b82018360208201111561068057600080fd5b803590602001918460208302840111600160201b831117156106a157600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602081013590604001356001600160a01b0316610fba565b3480156106ff57600080fd5b5061017c600480360360e081101561071657600080fd5b506001600160a01b0381358116916020810135821691604082013591606081013591608082013515159160a08101359091169060c00135610ff1565b34801561075e57600080fd5b5061017c6004803603604081101561077557600080fd5b506001600160a01b038135169060200135611000565b34801561079757600080fd5b5061053d600480360360408110156107ae57600080fd5b506001600160a01b038135811691602001351661108c565b3480156107d257600080fd5b5061017c600480360360208110156107e957600080fd5b50356001600160a01b03166110ac565b61017c6004803603606081101561080f57600080fd5b810190602081018135600160201b81111561082957600080fd5b82018360208201111561083b57600080fd5b803590602001918460208302840111600160201b8311171561085c57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050823593505050602001356001600160a01b0316611120565b61017c600480360360c08110156108be57600080fd5b810190602081018135600160201b8111156108d857600080fd5b8201836020820111156108ea57600080fd5b803590602001918460208302840111600160201b8311171561090b57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550506001600160a01b0383351693505050602081013590604081013590606081013515159060800135611235565b34801561097757600080fd5b5061017c600480360360e081101561098e57600080fd5b810190602081018135600160201b8111156109a857600080fd5b8201836020820111156109ba57600080fd5b803590602001918460208302840111600160201b831117156109db57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550506001600160a01b0383351693505050602081013590604081013590606081013590608081013515159060a00135611325565b348015610a4d57600080fd5b5061017c60048036036020811015610a6457600080fd5b50356001600160a01b03166113d2565b348015610a8057600080fd5b5061017c60048036036020811015610a9757600080fd5b50356001600160a01b0316611400565b348015610ab357600080fd5b5061017c60048036036020811015610aca57600080fd5b50356001600160a01b0316611475565b348015610ae657600080fd5b506101986114ec565b348015610afb57600080fd5b506101986114fb565b6000546001600160a01b031681565b610b1c8361150a565b610b316001600160a01b0385168484846115e0565b50505050565b610b408561150a565b600354604080516348d91abf60e01b81526001600160a01b03888116600483015287811660248301528681166044830152606482018690528415156084830152915191909216916348d91abf9160a480830192600092919082900301818387803b158015610bad57600080fd5b505af1158015610bc1573d6000803e3d6000fd5b505050505050505050565b6000610bd78861150a565b6003546040805163082a084960e41b81526001600160a01b038b811660048301528a811660248301528981166044830152606482018990526084820188905286151560a483015285811660c4830152915191909216916382a084909160e48083019260209291908290030181600087803b158015610c5457600080fd5b505af1158015610c68573d6000803e3d6000fd5b505050506040513d6020811015610c7e57600080fd5b505198975050505050505050565b60015484516001600160a01b039091169085906000198101908110610cad57fe5b60200260200101516001600160a01b031614610cfe576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b610d48610d0961163a565b60035486516001600160a01b039091169086908890600090610d2757fe5b60200260200101516001600160a01b03166115e0909392919063ffffffff16565b6000610d5585843061163e565b9050610d61818361177e565b6000805160206123388339815191523386600081518110610d7e57fe5b602002602001015187600189510381518110610d9657fe5b6020026020010151878560405180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019550505050505060405180910390a15050505050565b60015488516001600160a01b039091169089906000198101908110610e1457fe5b60200260200101516001600160a01b031614610e65576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b6000610e8a89600081518110610e7757fe5b60200260200101518989898930896117fb565b9050610ed3600360009054906101000a90046001600160a01b0316828b600081518110610eb357fe5b60200260200101516001600160a01b0316611a089092919063ffffffff16565b6000610ee08a843061163e565b9050610eec818661177e565b50505050505050505050565b3360009081526005602090815260408083206001600160a01b0394909416835292905220805460ff19166001179055565b6001546001600160a01b031681565b6000610f49888888888830886117fb565b9050610f55818461177e565b5050505050505050565b60046020526000908152604090205460ff1681565b6000610f8689600081518110610e7757fe5b9050610faf600360009054906101000a90046001600160a01b0316828b600081518110610eb357fe5b610eec89838661163e565b610fc5610d0961163a565b6000610fd285848461163e565b90506000805160206123388339815191523386600081518110610d7e57fe5b610f55878787878787876117fb565b61102261100b61163a565b6003546001600160a01b03858116929116846115e0565b60035460408051635f7bc11960e01b81526001600160a01b03858116600483015291519190921691635f7bc11991602480830192600092919082900301818387803b15801561107057600080fd5b505af1158015611084573d6000803e3d6000fd5b505050505050565b600560209081526000928352604080842090915290825290205460ff1681565b6000546001600160a01b031633146110ff576040805162461bcd60e51b81526020600482015260116024820152702937baba32b91d103337b93134b23232b760791b604482015290519081900360640190fd5b6001600160a01b03166000908152600460205260409020805460ff19169055565b60015483516001600160a01b0390911690849060009061113c57fe5b60200260200101516001600160a01b03161461118d576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b611195611a5f565b60006111a284848461163e565b905060008051602061233883398151915233856000815181106111c157fe5b6020026020010151866001885103815181106111d957fe5b6020026020010151348560405180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019550505050505060405180910390a150505050565b60015486516001600160a01b0390911690879060009061125157fe5b60200260200101516001600160a01b0316146112a2576040805162461bcd60e51b81526020600482015260156024820152600080516020612295833981519152604482015290519081900360640190fd5b34156112b0576112b0611a5f565b600186511180156112c15750600034115b156113015760006112d387863061163e565b90506112ff600360009054906101000a90046001600160a01b0316828960018b510381518110610eb357fe5b505b6110848660018851038151811061131457fe5b602002602001015186858585611ae8565b84156113545761135461133661163a565b60035489516001600160a01b039091169088908b90600090610d2757fe5b600187511180156113655750600085115b156113a557600061137788863061163e565b90506113a3600360009054906101000a90046001600160a01b0316828a60018c510381518110610eb357fe5b505b6113c9876001895103815181106113b857fe5b602002602001015187858585611ae8565b50505050505050565b3360009081526005602090815260408083206001600160a01b0394909416835292905220805460ff19169055565b6000546001600160a01b03163314611453576040805162461bcd60e51b81526020600482015260116024820152702937baba32b91d103337b93134b23232b760791b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b031633146114c8576040805162461bcd60e51b81526020600482015260116024820152702937baba32b91d103337b93134b23232b760791b604482015290519081900360640190fd5b6001600160a01b03166000908152600460205260409020805460ff19166001179055565b6002546001600160a01b031681565b6003546001600160a01b031681565b3360009081526004602052604090205460ff16611567576040805162461bcd60e51b81526020600482015260166024820152752937baba32b91d1034b73b30b634b21038363ab3b4b760511b604482015290519081900360640190fd5b6001600160a01b038116600090815260056020908152604080832033845290915290205460ff166115dd576040805162461bcd60e51b815260206004820152601b60248201527a149bdd5d195c8e881c1b1d59da5b881b9bdd08185c1c1c9bdd9959602a1b604482015290519081900360640190fd5b50565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610b31908590611ce3565b3390565b60008351600214156116855761167e8460008151811061165a57fe5b60200260200101518560018151811061166f57fe5b60200260200101518585611d94565b9050611777565b83516003141561172b5760006116c6856000815181106116a157fe5b6020026020010151866001815181106116b657fe5b6020026020010151600030611d94565b90506116ef600360009054906101000a90046001600160a01b03168287600181518110610eb357fe5b611723856001815181106116ff57fe5b60200260200101518660028151811061171457fe5b60200260200101518686611d94565b915050611777565b6040805162461bcd60e51b815260206004820152601c60248201527b0a4deeae8cae47440d2dcecc2d8d2c840bee0c2e8d05cd8cadccee8d60231b604482015290519081900360640190fd5b9392505050565b60015460408051632e1a7d4d60e01b81526004810185905290516001600160a01b0390921691632e1a7d4d9160248082019260009290919082900301818387803b1580156117cb57600080fd5b505af11580156117df573d6000803e3d6000fd5b506117f7925050506001600160a01b03821683611f92565b5050565b600083156118c157600354604080516340d3096b60e11b81526001600160a01b038a811660048301529151859392909216916381a612d691602480820192602092909190829003018186803b15801561185357600080fd5b505afa158015611867573d6000803e3d6000fd5b505050506040513d602081101561187d57600080fd5b505110156118bc5760405162461bcd60e51b81526004018080602001828103825260238152602001806123156023913960400191505060405180910390fd5b61197a565b60035460408051637092736960e11b81526001600160a01b038a8116600483015291518593929092169163e124e6d291602480820192602092909190829003018186803b15801561191157600080fd5b505afa158015611925573d6000803e3d6000fd5b505050506040513d602081101561193b57600080fd5b5051111561197a5760405162461bcd60e51b81526004018080602001828103825260248152602001806123826024913960400191505060405180910390fd5b6003546001600160a01b03166382a0849061199361163a565b604080516001600160e01b031960e085901b1681526001600160a01b039283166004820152828d166024820152828c166044820152606481018b9052608481018a905288151560a482015291871660c48301525160e48083019260209291908290030181600087803b158015610c5457600080fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611a5a908490611ce3565b505050565b600160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015611aaf57600080fd5b505af1158015611ac3573d6000803e3d6000fd5b5050600354600154611ae694506001600160a01b03908116935016905034611a08565b565b8115611bac5760035460408051637092736960e11b81526001600160a01b03878116600483015291518493929092169163e124e6d291602480820192602092909190829003018186803b158015611b3e57600080fd5b505afa158015611b52573d6000803e3d6000fd5b505050506040513d6020811015611b6857600080fd5b50511115611ba75760405162461bcd60e51b81526004018080602001828103825260248152602001806123826024913960400191505060405180910390fd5b611c65565b600354604080516340d3096b60e11b81526001600160a01b0387811660048301529151849392909216916381a612d691602480820192602092909190829003018186803b158015611bfc57600080fd5b505afa158015611c10573d6000803e3d6000fd5b505050506040513d6020811015611c2657600080fd5b50511015611c655760405162461bcd60e51b81526004018080602001828103825260238152602001806123156023913960400191505060405180910390fd5b6003546001600160a01b03166348d91abf611c7e61163a565b604080516001600160e01b031960e085901b1681526001600160a01b039283166004820152828a16602482015291881660448301526064820187905285151560848301525160a480830192600092919082900301818387803b158015610bad57600080fd5b6060611d38826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166120779092919063ffffffff16565b805190915015611a5a57808060200190516020811015611d5757600080fd5b5051611a5a5760405162461bcd60e51b815260040180806020018281038252602a815260200180612358602a913960400191505060405180910390fd5b60025460009081906001600160a01b0386811691161415611e39576003546040805163817bb85760e01b81526001600160a01b03898116600483015286811660248301529151919092169163817bb8579160448083019260209291908290030181600087803b158015611e0657600080fd5b505af1158015611e1a573d6000803e3d6000fd5b505050506040513d6020811015611e3057600080fd5b50519050611f34565b6002546001600160a01b0387811691161415611ea65760035460408051630711e61960e41b81526001600160a01b03888116600483015286811660248301529151919092169163711e61909160448083019260209291908290030181600087803b158015611e0657600080fd5b60035460408051634998b10960e11b81526001600160a01b038981166004830152888116602483015286811660448301529151919092169163933162129160648083019260209291908290030181600087803b158015611f0557600080fd5b505af1158015611f19573d6000803e3d6000fd5b505050506040513d6020811015611f2f57600080fd5b505190505b83811015611f89576040805162461bcd60e51b815260206004820152601e60248201527f526f757465723a20696e73756666696369656e7420616d6f756e744f75740000604482015290519081900360640190fd5b95945050505050565b80471015611fe7576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015290519081900360640190fd5b6040516000906001600160a01b0384169083908381818185875af1925050503d8060008114612032576040519150601f19603f3d011682016040523d82523d6000602084013e612037565b606091505b5050905080611a5a5760405162461bcd60e51b815260040180806020018281038252603a8152602001806122b5603a913960400191505060405180910390fd5b6060612086848460008561208e565b949350505050565b6060824710156120cf5760405162461bcd60e51b81526004018080602001828103825260268152602001806122ef6026913960400191505060405180910390fd5b6120d8856121ea565b612129576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106121685780518252601f199092019160209182019101612149565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146121ca576040519150601f19603f3d011682016040523d82523d6000602084013e6121cf565b606091505b50915091506121df8282866121f0565b979650505050505050565b3b151590565b606083156121ff575081611777565b82511561220f5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612259578181015183820152602001612241565b50505050905090810190601f1680156122865780820380516001836020036101000a031916815260200191505b509250505060405180910390fdfe526f757465723a20696e76616c6964205f706174680000000000000000000000416464726573733a20756e61626c6520746f2073656e642076616c75652c20726563697069656e74206d61792068617665207265766572746564416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c526f757465723a206d61726b207072696365206c6f776572207468616e206c696d6974cd3829a3813dc3cdd188fd3d01dcf3268c16be2fdd2dd21d0665418816e460625361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564526f757465723a206d61726b20707269636520686967686572207468616e206c696d6974a26469706673582212205fb07c28854d2a79764cfd95b47807b48d7af91fddcab27d57d2e9b67560698464736f6c634300060c0033000000000000000000000000489ee077994b6658eafa855c308275ead8097c4a00000000000000000000000045096e7aa921f27590f8f19e457794eb0967814100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1"; + public RouterDeploymentBase() : base(BYTECODE) { } + public RouterDeploymentBase(string byteCode) : base(byteCode) { } + [Parameter("address", "_vault", 1)] + public virtual string Vault { get; set; } + [Parameter("address", "_usdg", 2)] + public virtual string Usdg { get; set; } + [Parameter("address", "_weth", 3)] + public virtual string Weth { get; set; } + } + + public partial class AddPluginFunction : AddPluginFunctionBase { } + + [Function("addPlugin")] + public class AddPluginFunctionBase : FunctionMessage + { + [Parameter("address", "_plugin", 1)] + public virtual string Plugin { get; set; } + } + + public partial class ApprovePluginFunction : ApprovePluginFunctionBase { } + + [Function("approvePlugin")] + public class ApprovePluginFunctionBase : FunctionMessage + { + [Parameter("address", "_plugin", 1)] + public virtual string Plugin { get; set; } + } + + public partial class ApprovedPluginsFunction : ApprovedPluginsFunctionBase { } + + [Function("approvedPlugins", "bool")] + public class ApprovedPluginsFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + [Parameter("address", "", 2)] + public virtual string ReturnValue2 { get; set; } + } + + public partial class DecreasePositionFunction : DecreasePositionFunctionBase { } + + [Function("decreasePosition")] + public class DecreasePositionFunctionBase : FunctionMessage + { + [Parameter("address", "_collateralToken", 1)] + public virtual string CollateralToken { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_collateralDelta", 3)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("address", "_receiver", 6)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "_price", 7)] + public virtual BigInteger Price { get; set; } + } + + public partial class DecreasePositionAndSwapFunction : DecreasePositionAndSwapFunctionBase { } + + [Function("decreasePositionAndSwap")] + public class DecreasePositionAndSwapFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_collateralDelta", 3)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("address", "_receiver", 6)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "_price", 7)] + public virtual BigInteger Price { get; set; } + [Parameter("uint256", "_minOut", 8)] + public virtual BigInteger MinOut { get; set; } + } + + public partial class DecreasePositionAndSwapETHFunction : DecreasePositionAndSwapETHFunctionBase { } + + [Function("decreasePositionAndSwapETH")] + public class DecreasePositionAndSwapETHFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_collateralDelta", 3)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("address", "_receiver", 6)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "_price", 7)] + public virtual BigInteger Price { get; set; } + [Parameter("uint256", "_minOut", 8)] + public virtual BigInteger MinOut { get; set; } + } + + public partial class DecreasePositionETHFunction : DecreasePositionETHFunctionBase { } + + [Function("decreasePositionETH")] + public class DecreasePositionETHFunctionBase : FunctionMessage + { + [Parameter("address", "_collateralToken", 1)] + public virtual string CollateralToken { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_collateralDelta", 3)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("address", "_receiver", 6)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "_price", 7)] + public virtual BigInteger Price { get; set; } + } + + public partial class DenyPluginFunction : DenyPluginFunctionBase { } + + [Function("denyPlugin")] + public class DenyPluginFunctionBase : FunctionMessage + { + [Parameter("address", "_plugin", 1)] + public virtual string Plugin { get; set; } + } + + public partial class DirectPoolDepositFunction : DirectPoolDepositFunctionBase { } + + [Function("directPoolDeposit")] + public class DirectPoolDepositFunctionBase : FunctionMessage + { + [Parameter("address", "_token", 1)] + public virtual string Token { get; set; } + [Parameter("uint256", "_amount", 2)] + public virtual BigInteger Amount { get; set; } + } + + public partial class GovFunction : GovFunctionBase { } + + [Function("gov", "address")] + public class GovFunctionBase : FunctionMessage + { + + } + + public partial class IncreasePositionFunction : IncreasePositionFunctionBase { } + + [Function("increasePosition")] + public class IncreasePositionFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_amountIn", 3)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "_minOut", 4)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_sizeDelta", 5)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 6)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "_price", 7)] + public virtual BigInteger Price { get; set; } + } + + public partial class IncreasePositionETHFunction : IncreasePositionETHFunctionBase { } + + [Function("increasePositionETH")] + public class IncreasePositionETHFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("address", "_indexToken", 2)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_minOut", 3)] + public virtual BigInteger MinOut { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + [Parameter("uint256", "_price", 6)] + public virtual BigInteger Price { get; set; } + } + + public partial class PluginDecreasePositionFunction : PluginDecreasePositionFunctionBase { } + + [Function("pluginDecreasePosition", "uint256")] + public class PluginDecreasePositionFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("address", "_collateralToken", 2)] + public virtual string CollateralToken { get; set; } + [Parameter("address", "_indexToken", 3)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_collateralDelta", 4)] + public virtual BigInteger CollateralDelta { get; set; } + [Parameter("uint256", "_sizeDelta", 5)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 6)] + public virtual bool IsLong { get; set; } + [Parameter("address", "_receiver", 7)] + public virtual string Receiver { get; set; } + } + + public partial class PluginIncreasePositionFunction : PluginIncreasePositionFunctionBase { } + + [Function("pluginIncreasePosition")] + public class PluginIncreasePositionFunctionBase : FunctionMessage + { + [Parameter("address", "_account", 1)] + public virtual string Account { get; set; } + [Parameter("address", "_collateralToken", 2)] + public virtual string CollateralToken { get; set; } + [Parameter("address", "_indexToken", 3)] + public virtual string IndexToken { get; set; } + [Parameter("uint256", "_sizeDelta", 4)] + public virtual BigInteger SizeDelta { get; set; } + [Parameter("bool", "_isLong", 5)] + public virtual bool IsLong { get; set; } + } + + public partial class PluginTransferFunction : PluginTransferFunctionBase { } + + [Function("pluginTransfer")] + public class PluginTransferFunctionBase : FunctionMessage + { + [Parameter("address", "_token", 1)] + public virtual string Token { get; set; } + [Parameter("address", "_account", 2)] + public virtual string Account { get; set; } + [Parameter("address", "_receiver", 3)] + public virtual string Receiver { get; set; } + [Parameter("uint256", "_amount", 4)] + public virtual BigInteger Amount { get; set; } + } + + public partial class PluginsFunction : PluginsFunctionBase { } + + [Function("plugins", "bool")] + public class PluginsFunctionBase : FunctionMessage + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class RemovePluginFunction : RemovePluginFunctionBase { } + + [Function("removePlugin")] + public class RemovePluginFunctionBase : FunctionMessage + { + [Parameter("address", "_plugin", 1)] + public virtual string Plugin { get; set; } + } + + public partial class SetGovFunction : SetGovFunctionBase { } + + [Function("setGov")] + public class SetGovFunctionBase : FunctionMessage + { + [Parameter("address", "_gov", 1)] + public virtual string Gov { get; set; } + } + + public partial class SwapFunction : SwapFunctionBase { } + + [Function("swap")] + public class SwapFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("uint256", "_amountIn", 2)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "_minOut", 3)] + public virtual BigInteger MinOut { get; set; } + [Parameter("address", "_receiver", 4)] + public virtual string Receiver { get; set; } + } + + public partial class SwapETHToTokensFunction : SwapETHToTokensFunctionBase { } + + [Function("swapETHToTokens")] + public class SwapETHToTokensFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("uint256", "_minOut", 2)] + public virtual BigInteger MinOut { get; set; } + [Parameter("address", "_receiver", 3)] + public virtual string Receiver { get; set; } + } + + public partial class SwapTokensToETHFunction : SwapTokensToETHFunctionBase { } + + [Function("swapTokensToETH")] + public class SwapTokensToETHFunctionBase : FunctionMessage + { + [Parameter("address[]", "_path", 1)] + public virtual List Path { get; set; } + [Parameter("uint256", "_amountIn", 2)] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "_minOut", 3)] + public virtual BigInteger MinOut { get; set; } + [Parameter("address", "_receiver", 4)] + public virtual string Receiver { get; set; } + } + + public partial class UsdgFunction : UsdgFunctionBase { } + + [Function("usdg", "address")] + public class UsdgFunctionBase : FunctionMessage + { + + } + + public partial class VaultFunction : VaultFunctionBase { } + + [Function("vault", "address")] + public class VaultFunctionBase : FunctionMessage + { + + } + + public partial class WethFunction : WethFunctionBase { } + + [Function("weth", "address")] + public class WethFunctionBase : FunctionMessage + { + + } + + public partial class SwapEventDTO : SwapEventDTOBase { } + + [Event("Swap")] + public class SwapEventDTOBase : IEventDTO + { + [Parameter("address", "account", 1, false )] + public virtual string Account { get; set; } + [Parameter("address", "tokenIn", 2, false )] + public virtual string TokenIn { get; set; } + [Parameter("address", "tokenOut", 3, false )] + public virtual string TokenOut { get; set; } + [Parameter("uint256", "amountIn", 4, false )] + public virtual BigInteger AmountIn { get; set; } + [Parameter("uint256", "amountOut", 5, false )] + public virtual BigInteger AmountOut { get; set; } + } + + + + + + public partial class ApprovedPluginsOutputDTO : ApprovedPluginsOutputDTOBase { } + + [FunctionOutput] + public class ApprovedPluginsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bool", "", 1)] + public virtual bool ReturnValue1 { get; set; } + } + + + + + + + + + + + + + + public partial class GovOutputDTO : GovOutputDTOBase { } + + [FunctionOutput] + public class GovOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + + + + + + + + + + + public partial class PluginsOutputDTO : PluginsOutputDTOBase { } + + [FunctionOutput] + public class PluginsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("bool", "", 1)] + public virtual bool ReturnValue1 { get; set; } + } + + + + + + + + + + + + public partial class UsdgOutputDTO : UsdgOutputDTOBase { } + + [FunctionOutput] + public class UsdgOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class VaultOutputDTO : VaultOutputDTOBase { } + + [FunctionOutput] + public class VaultOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class WethOutputDTO : WethOutputDTOBase { } + + [FunctionOutput] + public class WethOutputDTOBase : IFunctionOutputDTO + { + [Parameter("address", "", 1)] + public virtual string ReturnValue1 { get; set; } + } +} diff --git a/src/Managing.Tools.ABI/Router/RouterService.cs b/src/Managing.Tools.ABI/Router/RouterService.cs new file mode 100644 index 0000000..934f189 --- /dev/null +++ b/src/Managing.Tools.ABI/Router/RouterService.cs @@ -0,0 +1,702 @@ +using System.Numerics; +using Nethereum.Web3; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.Contracts.ContractHandlers; +using Managing.Tools.Router.ContractDefinition; + +namespace Managing.Tools.Router +{ + public partial class RouterService + { + public static Task DeployContractAndWaitForReceiptAsync(Web3 web3, RouterDeployment routerDeployment, CancellationTokenSource cancellationTokenSource = null) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAndWaitForReceiptAsync(routerDeployment, cancellationTokenSource); + } + + public static Task DeployContractAsync(Web3 web3, RouterDeployment routerDeployment) + { + return web3.Eth.GetContractDeploymentHandler().SendRequestAsync(routerDeployment); + } + + public static async Task DeployContractAndGetServiceAsync(Web3 web3, RouterDeployment routerDeployment, CancellationTokenSource cancellationTokenSource = null) + { + var receipt = await DeployContractAndWaitForReceiptAsync(web3, routerDeployment, cancellationTokenSource); + return new RouterService(web3, receipt.ContractAddress); + } + + protected IWeb3 Web3 { get; } + + public ContractHandler ContractHandler { get; } + + public RouterService(Web3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public RouterService(IWeb3 web3, string contractAddress) + { + Web3 = web3; + ContractHandler = web3.Eth.GetContractHandler(contractAddress); + } + + public Task AddPluginRequestAsync(AddPluginFunction addPluginFunction) + { + return ContractHandler.SendRequestAsync(addPluginFunction); + } + + public Task AddPluginRequestAndWaitForReceiptAsync(AddPluginFunction addPluginFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(addPluginFunction, cancellationToken); + } + + public Task AddPluginRequestAsync(string plugin) + { + var addPluginFunction = new AddPluginFunction(); + addPluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAsync(addPluginFunction); + } + + public Task AddPluginRequestAndWaitForReceiptAsync(string plugin, CancellationTokenSource cancellationToken = null) + { + var addPluginFunction = new AddPluginFunction(); + addPluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(addPluginFunction, cancellationToken); + } + + public Task ApprovePluginRequestAsync(ApprovePluginFunction approvePluginFunction) + { + return ContractHandler.SendRequestAsync(approvePluginFunction); + } + + public Task ApprovePluginRequestAndWaitForReceiptAsync(ApprovePluginFunction approvePluginFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(approvePluginFunction, cancellationToken); + } + + public Task ApprovePluginRequestAsync(string plugin) + { + var approvePluginFunction = new ApprovePluginFunction(); + approvePluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAsync(approvePluginFunction); + } + + public Task ApprovePluginRequestAndWaitForReceiptAsync(string plugin, CancellationTokenSource cancellationToken = null) + { + var approvePluginFunction = new ApprovePluginFunction(); + approvePluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(approvePluginFunction, cancellationToken); + } + + public Task ApprovedPluginsQueryAsync(ApprovedPluginsFunction approvedPluginsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(approvedPluginsFunction, blockParameter); + } + + + public Task ApprovedPluginsQueryAsync(string returnValue1, string returnValue2, BlockParameter blockParameter = null) + { + var approvedPluginsFunction = new ApprovedPluginsFunction(); + approvedPluginsFunction.ReturnValue1 = returnValue1; + approvedPluginsFunction.ReturnValue2 = returnValue2; + + return ContractHandler.QueryAsync(approvedPluginsFunction, blockParameter); + } + + public Task DecreasePositionRequestAsync(DecreasePositionFunction decreasePositionFunction) + { + return ContractHandler.SendRequestAsync(decreasePositionFunction); + } + + public Task DecreasePositionRequestAndWaitForReceiptAsync(DecreasePositionFunction decreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionFunction, cancellationToken); + } + + public Task DecreasePositionRequestAsync(string collateralToken, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price) + { + var decreasePositionFunction = new DecreasePositionFunction(); + decreasePositionFunction.CollateralToken = collateralToken; + decreasePositionFunction.IndexToken = indexToken; + decreasePositionFunction.CollateralDelta = collateralDelta; + decreasePositionFunction.SizeDelta = sizeDelta; + decreasePositionFunction.IsLong = isLong; + decreasePositionFunction.Receiver = receiver; + decreasePositionFunction.Price = price; + + return ContractHandler.SendRequestAsync(decreasePositionFunction); + } + + public Task DecreasePositionRequestAndWaitForReceiptAsync(string collateralToken, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price, CancellationTokenSource cancellationToken = null) + { + var decreasePositionFunction = new DecreasePositionFunction(); + decreasePositionFunction.CollateralToken = collateralToken; + decreasePositionFunction.IndexToken = indexToken; + decreasePositionFunction.CollateralDelta = collateralDelta; + decreasePositionFunction.SizeDelta = sizeDelta; + decreasePositionFunction.IsLong = isLong; + decreasePositionFunction.Receiver = receiver; + decreasePositionFunction.Price = price; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionFunction, cancellationToken); + } + + public Task DecreasePositionAndSwapRequestAsync(DecreasePositionAndSwapFunction decreasePositionAndSwapFunction) + { + return ContractHandler.SendRequestAsync(decreasePositionAndSwapFunction); + } + + public Task DecreasePositionAndSwapRequestAndWaitForReceiptAsync(DecreasePositionAndSwapFunction decreasePositionAndSwapFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionAndSwapFunction, cancellationToken); + } + + public Task DecreasePositionAndSwapRequestAsync(List path, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price, BigInteger minOut) + { + var decreasePositionAndSwapFunction = new DecreasePositionAndSwapFunction(); + decreasePositionAndSwapFunction.Path = path; + decreasePositionAndSwapFunction.IndexToken = indexToken; + decreasePositionAndSwapFunction.CollateralDelta = collateralDelta; + decreasePositionAndSwapFunction.SizeDelta = sizeDelta; + decreasePositionAndSwapFunction.IsLong = isLong; + decreasePositionAndSwapFunction.Receiver = receiver; + decreasePositionAndSwapFunction.Price = price; + decreasePositionAndSwapFunction.MinOut = minOut; + + return ContractHandler.SendRequestAsync(decreasePositionAndSwapFunction); + } + + public Task DecreasePositionAndSwapRequestAndWaitForReceiptAsync(List path, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price, BigInteger minOut, CancellationTokenSource cancellationToken = null) + { + var decreasePositionAndSwapFunction = new DecreasePositionAndSwapFunction(); + decreasePositionAndSwapFunction.Path = path; + decreasePositionAndSwapFunction.IndexToken = indexToken; + decreasePositionAndSwapFunction.CollateralDelta = collateralDelta; + decreasePositionAndSwapFunction.SizeDelta = sizeDelta; + decreasePositionAndSwapFunction.IsLong = isLong; + decreasePositionAndSwapFunction.Receiver = receiver; + decreasePositionAndSwapFunction.Price = price; + decreasePositionAndSwapFunction.MinOut = minOut; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionAndSwapFunction, cancellationToken); + } + + public Task DecreasePositionAndSwapETHRequestAsync(DecreasePositionAndSwapETHFunction decreasePositionAndSwapETHFunction) + { + return ContractHandler.SendRequestAsync(decreasePositionAndSwapETHFunction); + } + + public Task DecreasePositionAndSwapETHRequestAndWaitForReceiptAsync(DecreasePositionAndSwapETHFunction decreasePositionAndSwapETHFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionAndSwapETHFunction, cancellationToken); + } + + public Task DecreasePositionAndSwapETHRequestAsync(List path, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price, BigInteger minOut) + { + var decreasePositionAndSwapETHFunction = new DecreasePositionAndSwapETHFunction(); + decreasePositionAndSwapETHFunction.Path = path; + decreasePositionAndSwapETHFunction.IndexToken = indexToken; + decreasePositionAndSwapETHFunction.CollateralDelta = collateralDelta; + decreasePositionAndSwapETHFunction.SizeDelta = sizeDelta; + decreasePositionAndSwapETHFunction.IsLong = isLong; + decreasePositionAndSwapETHFunction.Receiver = receiver; + decreasePositionAndSwapETHFunction.Price = price; + decreasePositionAndSwapETHFunction.MinOut = minOut; + + return ContractHandler.SendRequestAsync(decreasePositionAndSwapETHFunction); + } + + public Task DecreasePositionAndSwapETHRequestAndWaitForReceiptAsync(List path, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price, BigInteger minOut, CancellationTokenSource cancellationToken = null) + { + var decreasePositionAndSwapETHFunction = new DecreasePositionAndSwapETHFunction(); + decreasePositionAndSwapETHFunction.Path = path; + decreasePositionAndSwapETHFunction.IndexToken = indexToken; + decreasePositionAndSwapETHFunction.CollateralDelta = collateralDelta; + decreasePositionAndSwapETHFunction.SizeDelta = sizeDelta; + decreasePositionAndSwapETHFunction.IsLong = isLong; + decreasePositionAndSwapETHFunction.Receiver = receiver; + decreasePositionAndSwapETHFunction.Price = price; + decreasePositionAndSwapETHFunction.MinOut = minOut; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionAndSwapETHFunction, cancellationToken); + } + + public Task DecreasePositionETHRequestAsync(DecreasePositionETHFunction decreasePositionETHFunction) + { + return ContractHandler.SendRequestAsync(decreasePositionETHFunction); + } + + public Task DecreasePositionETHRequestAndWaitForReceiptAsync(DecreasePositionETHFunction decreasePositionETHFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionETHFunction, cancellationToken); + } + + public Task DecreasePositionETHRequestAsync(string collateralToken, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price) + { + var decreasePositionETHFunction = new DecreasePositionETHFunction(); + decreasePositionETHFunction.CollateralToken = collateralToken; + decreasePositionETHFunction.IndexToken = indexToken; + decreasePositionETHFunction.CollateralDelta = collateralDelta; + decreasePositionETHFunction.SizeDelta = sizeDelta; + decreasePositionETHFunction.IsLong = isLong; + decreasePositionETHFunction.Receiver = receiver; + decreasePositionETHFunction.Price = price; + + return ContractHandler.SendRequestAsync(decreasePositionETHFunction); + } + + public Task DecreasePositionETHRequestAndWaitForReceiptAsync(string collateralToken, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, BigInteger price, CancellationTokenSource cancellationToken = null) + { + var decreasePositionETHFunction = new DecreasePositionETHFunction(); + decreasePositionETHFunction.CollateralToken = collateralToken; + decreasePositionETHFunction.IndexToken = indexToken; + decreasePositionETHFunction.CollateralDelta = collateralDelta; + decreasePositionETHFunction.SizeDelta = sizeDelta; + decreasePositionETHFunction.IsLong = isLong; + decreasePositionETHFunction.Receiver = receiver; + decreasePositionETHFunction.Price = price; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(decreasePositionETHFunction, cancellationToken); + } + + public Task DenyPluginRequestAsync(DenyPluginFunction denyPluginFunction) + { + return ContractHandler.SendRequestAsync(denyPluginFunction); + } + + public Task DenyPluginRequestAndWaitForReceiptAsync(DenyPluginFunction denyPluginFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(denyPluginFunction, cancellationToken); + } + + public Task DenyPluginRequestAsync(string plugin) + { + var denyPluginFunction = new DenyPluginFunction(); + denyPluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAsync(denyPluginFunction); + } + + public Task DenyPluginRequestAndWaitForReceiptAsync(string plugin, CancellationTokenSource cancellationToken = null) + { + var denyPluginFunction = new DenyPluginFunction(); + denyPluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(denyPluginFunction, cancellationToken); + } + + public Task DirectPoolDepositRequestAsync(DirectPoolDepositFunction directPoolDepositFunction) + { + return ContractHandler.SendRequestAsync(directPoolDepositFunction); + } + + public Task DirectPoolDepositRequestAndWaitForReceiptAsync(DirectPoolDepositFunction directPoolDepositFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(directPoolDepositFunction, cancellationToken); + } + + public Task DirectPoolDepositRequestAsync(string token, BigInteger amount) + { + var directPoolDepositFunction = new DirectPoolDepositFunction(); + directPoolDepositFunction.Token = token; + directPoolDepositFunction.Amount = amount; + + return ContractHandler.SendRequestAsync(directPoolDepositFunction); + } + + public Task DirectPoolDepositRequestAndWaitForReceiptAsync(string token, BigInteger amount, CancellationTokenSource cancellationToken = null) + { + var directPoolDepositFunction = new DirectPoolDepositFunction(); + directPoolDepositFunction.Token = token; + directPoolDepositFunction.Amount = amount; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(directPoolDepositFunction, cancellationToken); + } + + public Task GovQueryAsync(GovFunction govFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(govFunction, blockParameter); + } + + + public Task GovQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task IncreasePositionRequestAsync(IncreasePositionFunction increasePositionFunction) + { + return ContractHandler.SendRequestAsync(increasePositionFunction); + } + + public Task IncreasePositionRequestAndWaitForReceiptAsync(IncreasePositionFunction increasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(increasePositionFunction, cancellationToken); + } + + public Task IncreasePositionRequestAsync(List path, string indexToken, BigInteger amountIn, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger price) + { + var increasePositionFunction = new IncreasePositionFunction(); + increasePositionFunction.Path = path; + increasePositionFunction.IndexToken = indexToken; + increasePositionFunction.AmountIn = amountIn; + increasePositionFunction.MinOut = minOut; + increasePositionFunction.SizeDelta = sizeDelta; + increasePositionFunction.IsLong = isLong; + increasePositionFunction.Price = price; + + return ContractHandler.SendRequestAsync(increasePositionFunction); + } + + public Task IncreasePositionRequestAndWaitForReceiptAsync(List path, string indexToken, BigInteger amountIn, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger price, CancellationTokenSource cancellationToken = null) + { + var increasePositionFunction = new IncreasePositionFunction(); + increasePositionFunction.Path = path; + increasePositionFunction.IndexToken = indexToken; + increasePositionFunction.AmountIn = amountIn; + increasePositionFunction.MinOut = minOut; + increasePositionFunction.SizeDelta = sizeDelta; + increasePositionFunction.IsLong = isLong; + increasePositionFunction.Price = price; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(increasePositionFunction, cancellationToken); + } + + public Task IncreasePositionETHRequestAsync(IncreasePositionETHFunction increasePositionETHFunction) + { + return ContractHandler.SendRequestAsync(increasePositionETHFunction); + } + + public Task IncreasePositionETHRequestAndWaitForReceiptAsync(IncreasePositionETHFunction increasePositionETHFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(increasePositionETHFunction, cancellationToken); + } + + public Task IncreasePositionETHRequestAsync(List path, string indexToken, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger price) + { + var increasePositionETHFunction = new IncreasePositionETHFunction(); + increasePositionETHFunction.Path = path; + increasePositionETHFunction.IndexToken = indexToken; + increasePositionETHFunction.MinOut = minOut; + increasePositionETHFunction.SizeDelta = sizeDelta; + increasePositionETHFunction.IsLong = isLong; + increasePositionETHFunction.Price = price; + + return ContractHandler.SendRequestAsync(increasePositionETHFunction); + } + + public Task IncreasePositionETHRequestAndWaitForReceiptAsync(List path, string indexToken, BigInteger minOut, BigInteger sizeDelta, bool isLong, BigInteger price, CancellationTokenSource cancellationToken = null) + { + var increasePositionETHFunction = new IncreasePositionETHFunction(); + increasePositionETHFunction.Path = path; + increasePositionETHFunction.IndexToken = indexToken; + increasePositionETHFunction.MinOut = minOut; + increasePositionETHFunction.SizeDelta = sizeDelta; + increasePositionETHFunction.IsLong = isLong; + increasePositionETHFunction.Price = price; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(increasePositionETHFunction, cancellationToken); + } + + public Task PluginDecreasePositionRequestAsync(PluginDecreasePositionFunction pluginDecreasePositionFunction) + { + return ContractHandler.SendRequestAsync(pluginDecreasePositionFunction); + } + + public Task PluginDecreasePositionRequestAndWaitForReceiptAsync(PluginDecreasePositionFunction pluginDecreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(pluginDecreasePositionFunction, cancellationToken); + } + + public Task PluginDecreasePositionRequestAsync(string account, string collateralToken, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver) + { + var pluginDecreasePositionFunction = new PluginDecreasePositionFunction(); + pluginDecreasePositionFunction.Account = account; + pluginDecreasePositionFunction.CollateralToken = collateralToken; + pluginDecreasePositionFunction.IndexToken = indexToken; + pluginDecreasePositionFunction.CollateralDelta = collateralDelta; + pluginDecreasePositionFunction.SizeDelta = sizeDelta; + pluginDecreasePositionFunction.IsLong = isLong; + pluginDecreasePositionFunction.Receiver = receiver; + + return ContractHandler.SendRequestAsync(pluginDecreasePositionFunction); + } + + public Task PluginDecreasePositionRequestAndWaitForReceiptAsync(string account, string collateralToken, string indexToken, BigInteger collateralDelta, BigInteger sizeDelta, bool isLong, string receiver, CancellationTokenSource cancellationToken = null) + { + var pluginDecreasePositionFunction = new PluginDecreasePositionFunction(); + pluginDecreasePositionFunction.Account = account; + pluginDecreasePositionFunction.CollateralToken = collateralToken; + pluginDecreasePositionFunction.IndexToken = indexToken; + pluginDecreasePositionFunction.CollateralDelta = collateralDelta; + pluginDecreasePositionFunction.SizeDelta = sizeDelta; + pluginDecreasePositionFunction.IsLong = isLong; + pluginDecreasePositionFunction.Receiver = receiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(pluginDecreasePositionFunction, cancellationToken); + } + + public Task PluginIncreasePositionRequestAsync(PluginIncreasePositionFunction pluginIncreasePositionFunction) + { + return ContractHandler.SendRequestAsync(pluginIncreasePositionFunction); + } + + public Task PluginIncreasePositionRequestAndWaitForReceiptAsync(PluginIncreasePositionFunction pluginIncreasePositionFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(pluginIncreasePositionFunction, cancellationToken); + } + + public Task PluginIncreasePositionRequestAsync(string account, string collateralToken, string indexToken, BigInteger sizeDelta, bool isLong) + { + var pluginIncreasePositionFunction = new PluginIncreasePositionFunction(); + pluginIncreasePositionFunction.Account = account; + pluginIncreasePositionFunction.CollateralToken = collateralToken; + pluginIncreasePositionFunction.IndexToken = indexToken; + pluginIncreasePositionFunction.SizeDelta = sizeDelta; + pluginIncreasePositionFunction.IsLong = isLong; + + return ContractHandler.SendRequestAsync(pluginIncreasePositionFunction); + } + + public Task PluginIncreasePositionRequestAndWaitForReceiptAsync(string account, string collateralToken, string indexToken, BigInteger sizeDelta, bool isLong, CancellationTokenSource cancellationToken = null) + { + var pluginIncreasePositionFunction = new PluginIncreasePositionFunction(); + pluginIncreasePositionFunction.Account = account; + pluginIncreasePositionFunction.CollateralToken = collateralToken; + pluginIncreasePositionFunction.IndexToken = indexToken; + pluginIncreasePositionFunction.SizeDelta = sizeDelta; + pluginIncreasePositionFunction.IsLong = isLong; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(pluginIncreasePositionFunction, cancellationToken); + } + + public Task PluginTransferRequestAsync(PluginTransferFunction pluginTransferFunction) + { + return ContractHandler.SendRequestAsync(pluginTransferFunction); + } + + public Task PluginTransferRequestAndWaitForReceiptAsync(PluginTransferFunction pluginTransferFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(pluginTransferFunction, cancellationToken); + } + + public Task PluginTransferRequestAsync(string token, string account, string receiver, BigInteger amount) + { + var pluginTransferFunction = new PluginTransferFunction(); + pluginTransferFunction.Token = token; + pluginTransferFunction.Account = account; + pluginTransferFunction.Receiver = receiver; + pluginTransferFunction.Amount = amount; + + return ContractHandler.SendRequestAsync(pluginTransferFunction); + } + + public Task PluginTransferRequestAndWaitForReceiptAsync(string token, string account, string receiver, BigInteger amount, CancellationTokenSource cancellationToken = null) + { + var pluginTransferFunction = new PluginTransferFunction(); + pluginTransferFunction.Token = token; + pluginTransferFunction.Account = account; + pluginTransferFunction.Receiver = receiver; + pluginTransferFunction.Amount = amount; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(pluginTransferFunction, cancellationToken); + } + + public Task PluginsQueryAsync(PluginsFunction pluginsFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(pluginsFunction, blockParameter); + } + + + public Task PluginsQueryAsync(string returnValue1, BlockParameter blockParameter = null) + { + var pluginsFunction = new PluginsFunction(); + pluginsFunction.ReturnValue1 = returnValue1; + + return ContractHandler.QueryAsync(pluginsFunction, blockParameter); + } + + public Task RemovePluginRequestAsync(RemovePluginFunction removePluginFunction) + { + return ContractHandler.SendRequestAsync(removePluginFunction); + } + + public Task RemovePluginRequestAndWaitForReceiptAsync(RemovePluginFunction removePluginFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(removePluginFunction, cancellationToken); + } + + public Task RemovePluginRequestAsync(string plugin) + { + var removePluginFunction = new RemovePluginFunction(); + removePluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAsync(removePluginFunction); + } + + public Task RemovePluginRequestAndWaitForReceiptAsync(string plugin, CancellationTokenSource cancellationToken = null) + { + var removePluginFunction = new RemovePluginFunction(); + removePluginFunction.Plugin = plugin; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(removePluginFunction, cancellationToken); + } + + public Task SetGovRequestAsync(SetGovFunction setGovFunction) + { + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(SetGovFunction setGovFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + + public Task SetGovRequestAsync(string gov) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAsync(setGovFunction); + } + + public Task SetGovRequestAndWaitForReceiptAsync(string gov, CancellationTokenSource cancellationToken = null) + { + var setGovFunction = new SetGovFunction(); + setGovFunction.Gov = gov; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(setGovFunction, cancellationToken); + } + + public Task SwapRequestAsync(SwapFunction swapFunction) + { + return ContractHandler.SendRequestAsync(swapFunction); + } + + public Task SwapRequestAndWaitForReceiptAsync(SwapFunction swapFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(swapFunction, cancellationToken); + } + + public Task SwapRequestAsync(List path, BigInteger amountIn, BigInteger minOut, string receiver) + { + var swapFunction = new SwapFunction(); + swapFunction.Path = path; + swapFunction.AmountIn = amountIn; + swapFunction.MinOut = minOut; + swapFunction.Receiver = receiver; + + return ContractHandler.SendRequestAsync(swapFunction); + } + + public Task SwapRequestAndWaitForReceiptAsync(List path, BigInteger amountIn, BigInteger minOut, string receiver, CancellationTokenSource cancellationToken = null) + { + var swapFunction = new SwapFunction(); + swapFunction.Path = path; + swapFunction.AmountIn = amountIn; + swapFunction.MinOut = minOut; + swapFunction.Receiver = receiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(swapFunction, cancellationToken); + } + + public Task SwapETHToTokensRequestAsync(SwapETHToTokensFunction swapETHToTokensFunction) + { + return ContractHandler.SendRequestAsync(swapETHToTokensFunction); + } + + public Task SwapETHToTokensRequestAndWaitForReceiptAsync(SwapETHToTokensFunction swapETHToTokensFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(swapETHToTokensFunction, cancellationToken); + } + + public Task SwapETHToTokensRequestAsync(List path, BigInteger minOut, string receiver) + { + var swapETHToTokensFunction = new SwapETHToTokensFunction(); + swapETHToTokensFunction.Path = path; + swapETHToTokensFunction.MinOut = minOut; + swapETHToTokensFunction.Receiver = receiver; + + return ContractHandler.SendRequestAsync(swapETHToTokensFunction); + } + + public Task SwapETHToTokensRequestAndWaitForReceiptAsync(List path, BigInteger minOut, string receiver, CancellationTokenSource cancellationToken = null) + { + var swapETHToTokensFunction = new SwapETHToTokensFunction(); + swapETHToTokensFunction.Path = path; + swapETHToTokensFunction.MinOut = minOut; + swapETHToTokensFunction.Receiver = receiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(swapETHToTokensFunction, cancellationToken); + } + + public Task SwapTokensToETHRequestAsync(SwapTokensToETHFunction swapTokensToETHFunction) + { + return ContractHandler.SendRequestAsync(swapTokensToETHFunction); + } + + public Task SwapTokensToETHRequestAndWaitForReceiptAsync(SwapTokensToETHFunction swapTokensToETHFunction, CancellationTokenSource cancellationToken = null) + { + return ContractHandler.SendRequestAndWaitForReceiptAsync(swapTokensToETHFunction, cancellationToken); + } + + public Task SwapTokensToETHRequestAsync(List path, BigInteger amountIn, BigInteger minOut, string receiver) + { + var swapTokensToETHFunction = new SwapTokensToETHFunction(); + swapTokensToETHFunction.Path = path; + swapTokensToETHFunction.AmountIn = amountIn; + swapTokensToETHFunction.MinOut = minOut; + swapTokensToETHFunction.Receiver = receiver; + + return ContractHandler.SendRequestAsync(swapTokensToETHFunction); + } + + public Task SwapTokensToETHRequestAndWaitForReceiptAsync(List path, BigInteger amountIn, BigInteger minOut, string receiver, CancellationTokenSource cancellationToken = null) + { + var swapTokensToETHFunction = new SwapTokensToETHFunction(); + swapTokensToETHFunction.Path = path; + swapTokensToETHFunction.AmountIn = amountIn; + swapTokensToETHFunction.MinOut = minOut; + swapTokensToETHFunction.Receiver = receiver; + + return ContractHandler.SendRequestAndWaitForReceiptAsync(swapTokensToETHFunction, cancellationToken); + } + + public Task UsdgQueryAsync(UsdgFunction usdgFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(usdgFunction, blockParameter); + } + + + public Task UsdgQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task VaultQueryAsync(VaultFunction vaultFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(vaultFunction, blockParameter); + } + + + public Task VaultQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + + public Task WethQueryAsync(WethFunction wethFunction, BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(wethFunction, blockParameter); + } + + + public Task WethQueryAsync(BlockParameter blockParameter = null) + { + return ContractHandler.QueryAsync(null, blockParameter); + } + } +} diff --git a/src/Managing.WebApp/.env b/src/Managing.WebApp/.env new file mode 100644 index 0000000..a673da2 --- /dev/null +++ b/src/Managing.WebApp/.env @@ -0,0 +1,6 @@ +VITE_API_URL_LOCAL=https://localhost:5001 +VITE_API_URL_SERVER=https://localhost +VITE_WORKER_URL_LOCAL=https://localhost:5002 +VITE_WORKER_URL_SERVER=https://localhost:444 +ALCHEMY_ID=Bao7OirVe4bmYiDbPh0l8cs5gYb5D4_9 +WALLET_CONNECT_PROJECT_ID=363bf09c10fec2293b21ee199b2ce8d5 \ No newline at end of file diff --git a/src/Managing.WebApp/.eslintignore b/src/Managing.WebApp/.eslintignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/src/Managing.WebApp/.eslintignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/src/Managing.WebApp/.eslintrc b/src/Managing.WebApp/.eslintrc new file mode 100644 index 0000000..2d7c78b --- /dev/null +++ b/src/Managing.WebApp/.eslintrc @@ -0,0 +1,98 @@ +{ + "root": true, + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:jsx-a11y/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": [ + "jsx-a11y", + "import", + "sort-keys-fix", + "react-hooks", + "@typescript-eslint", + "prettier" + ], + "env": { + "browser": true, + "node": true, + "es6": true, + "jest": true + }, + "globals": { + "JSX": "readonly" + }, + "settings": { + "react": { + "version": "detect" + }, + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx"] + }, + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + }, + "typescript": { + "alwaysTryTypes": true, + // always try to resolve types under `@types` directory even it doesn't contain any source code, like `@types/unist` + "project": ["tsconfig.json"] + } + } + }, + "rules": { + "no-alert": "error", + "no-console": "error", + "react-hooks/rules-of-hooks": "error", + "prettier/prettier": [ + "warn", + {}, + { + "properties": { + "usePrettierrc": true + } + } + ], + "import/order": [ + "warn", + { + "groups": [ + "builtin", + "external", + "internal", + "parent", + "sibling", + "index", + "object" + ], + "newlines-between": "always", + "alphabetize": { + "order": "asc", + "caseInsensitive": true + } + } + ], + "import/named": "error", + "import/default": "error", + "import/export": "error", + "import/no-named-as-default": "warn", + "import/no-duplicates": "error", + "sort-keys-fix/sort-keys-fix": "warn", + "@import/no-named-as-default-member": "off", + "@typescript-eslint/consistent-type-imports": "warn", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-empty-function": "off" + }, + "overrides": [ + { + "files": ["*.js"], + "rules": { + "@typescript-eslint/explicit-module-boundary-types": ["off"], + "@typescript-eslint/no-var-requires": ["off"] + } + } + ] +} diff --git a/src/Managing.WebApp/.gitattributes b/src/Managing.WebApp/.gitattributes new file mode 100644 index 0000000..2dd6f4d --- /dev/null +++ b/src/Managing.WebApp/.gitattributes @@ -0,0 +1,3 @@ +.jest/* linguist-vendored +mocks/* linguist-vendored +mockServiceWorker.js linguist-vendored diff --git a/src/Managing.WebApp/.github/workflows/build.yml b/src/Managing.WebApp/.github/workflows/build.yml new file mode 100644 index 0000000..cf9565c --- /dev/null +++ b/src/Managing.WebApp/.github/workflows/build.yml @@ -0,0 +1,18 @@ +name: Build +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2-beta + with: + node-version: '18.1.0' + - run: yarn install + - run: yarn build diff --git a/src/Managing.WebApp/.github/workflows/lint.yml b/src/Managing.WebApp/.github/workflows/lint.yml new file mode 100644 index 0000000..d84f5b7 --- /dev/null +++ b/src/Managing.WebApp/.github/workflows/lint.yml @@ -0,0 +1,18 @@ +name: Lint +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2-beta + with: + node-version: '18.1.0' + - run: yarn install + - run: yarn lint diff --git a/src/Managing.WebApp/.github/workflows/test.yml b/src/Managing.WebApp/.github/workflows/test.yml new file mode 100644 index 0000000..c409935 --- /dev/null +++ b/src/Managing.WebApp/.github/workflows/test.yml @@ -0,0 +1,18 @@ +name: Test +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2-beta + with: + node-version: '18.1.0' + - run: yarn install + - run: yarn test diff --git a/src/Managing.WebApp/.github/workflows/typecheck.yml b/src/Managing.WebApp/.github/workflows/typecheck.yml new file mode 100644 index 0000000..eeb1640 --- /dev/null +++ b/src/Managing.WebApp/.github/workflows/typecheck.yml @@ -0,0 +1,18 @@ +name: Typecheck +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2-beta + with: + node-version: '18.1.0' + - run: yarn install + - run: yarn typecheck diff --git a/src/Managing.WebApp/.gitignore b/src/Managing.WebApp/.gitignore new file mode 100644 index 0000000..d451ff1 --- /dev/null +++ b/src/Managing.WebApp/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/src/Managing.WebApp/.prettierignore b/src/Managing.WebApp/.prettierignore new file mode 100644 index 0000000..4969764 --- /dev/null +++ b/src/Managing.WebApp/.prettierignore @@ -0,0 +1,5 @@ +.git +node_modules +.eslintignore +.gitignore +LICENSE diff --git a/src/Managing.WebApp/.prettierrc b/src/Managing.WebApp/.prettierrc new file mode 100644 index 0000000..fd496a8 --- /dev/null +++ b/src/Managing.WebApp/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": false +} diff --git a/src/Managing.WebApp/Dockerfile b/src/Managing.WebApp/Dockerfile new file mode 100644 index 0000000..414406f --- /dev/null +++ b/src/Managing.WebApp/Dockerfile @@ -0,0 +1,20 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY . /app + +ENV NODE_ENV=production +ENV VITE_API_URL_LOCAL=https://localhost:5001 +ENV VITE_API_URL_SERVER=https://localhost + + +RUN npm install serve -g + +RUN npm install + +RUN npm run build + +EXPOSE 3000 + +CMD ["npm", "run", "serve"] \ No newline at end of file diff --git a/src/Managing.WebApp/LICENSE b/src/Managing.WebApp/LICENSE new file mode 100644 index 0000000..b229c5f --- /dev/null +++ b/src/Managing.WebApp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Managing + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/Managing.WebApp/README.md b/src/Managing.WebApp/README.md new file mode 100644 index 0000000..5be74be --- /dev/null +++ b/src/Managing.WebApp/README.md @@ -0,0 +1,94 @@ +# vite-react-ts-extended [![Typecheck](https://github.com/laststance/vite-react-ts-extended/actions/workflows/typecheck.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/typecheck.yml) [![Test](https://github.com/laststance/vite-react-ts-extended/actions/workflows/test.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/test.yml) [![Build](https://github.com/laststance/vite-react-ts-extended/actions/workflows/build.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/build.yml) [![Lint](https://github.com/laststance/vite-react-ts-extended/actions/workflows/lint.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/lint.yml) [![Depfu](https://badges.depfu.com/badges/6c7775918ccc8647160750e168617a65/overview.svg)](https://depfu.com/github/laststance/vite-react-ts-extended?project_id=32682) + +> My CRA alternative. +> Create plain and lightweight React+TS programming environment with familiar pre-setup tooling +> eslint/prettier, jest/TS/react-testing-library/msw, tailwindcss, CI. + +## [Trying this Online!](https://codesandbox.io/s/vite-react-ts-extended-cbgyfz?file=/src/App.tsx) + + + +This is the official [Vite](https://vitejs.dev/) template(`npm init vite@latest myapp -- --template react-ts`) and some extended setup. + +- [eslint-typescript](https://github.com/typescript-eslint/typescript-eslint) and [Prettier](https://prettier.io/) integration. Rules are 100% my personal setup 💅 +- [jest](https://jestjs.io/), [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/), [react-hooks-testing-library](https://github.com/testing-library/react-hooks-testing-library), [MSW](https://mswjs.io/) +- [tailwindcss](https://tailwindcss.com/) +- [Github Actions](https://github.com/features/actions) + +All npm package are keeping least release version powered by [Depfu](https://depfu.com/). + +# Installation + +``` +npx degit laststance/vite-react-ts-extended myapp +``` + +### yarn + +```sh +cd myapp +yarn install +yarn validate # The installation was successful if no error occurs after running 'validate'. +yarn dev +``` + +### npm + +```sh +cd myapp +npm install +npm run validate # The installation was successful if no error occurs after running 'validate'. +npm run dev +``` + +### Commands + +```sh +yarn dev # start development server +yarn validate # run test,lint,build,typecheck concurrently +yarn test # run jest +yarn lint # run eslint +yarn lint:fix # run eslint with --fix option +yarn typecheck # run TypeScript compiler check +yarn build # build production bundle to 'dist' directly +yarn prettier # run prettier for json|yml|css|md|mdx files +yarn clean # remove 'node_modules' 'yarn.lock' 'dist' completely +yarn serve # launch server for production bundle in local +``` + +# Background + +The evolution of the React framework is accelerating more than ever before. +[Next.js](https://nextjs.org/), [Remix](https://remix.run/), [RedwoodJS](https://redwoodjs.com/), [Gatsby](https://www.gatsbyjs.com/), [Blitz](https://blitzjs.com/) etc... + +Ahthough I still need plain React programming starter some reason. (.e.g Demo, Experiment like Deep Dive React Core.) +So far, [create-react-app](https://github.com/facebook/create-react-app) **was** it. +In short, [create-react-app](https://github.com/facebook/create-react-app) development couldn't say active. Please read the [Issue](https://github.com/facebook/create-react-app/issues/11180) in details. + +So I created an alternative to [create-react-app](https://github.com/facebook/create-react-app) for myself, based on [Vite](https://github.com/facebook/create-react-app). +This project contains my very opinionted setup, +but I hope it will be a useful tool for people who have similar needs to mine! 😀 + +# License + +MIT + +## Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + +

ryota-murakami

💻 📖 ⚠️
+ + + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/src/Managing.WebApp/index.html b/src/Managing.WebApp/index.html new file mode 100644 index 0000000..c6488fe --- /dev/null +++ b/src/Managing.WebApp/index.html @@ -0,0 +1,16 @@ + + + + + + + Managing + + +
+ + + + diff --git a/src/Managing.WebApp/jest.config.js b/src/Managing.WebApp/jest.config.js new file mode 100644 index 0000000..dd7f4d9 --- /dev/null +++ b/src/Managing.WebApp/jest.config.js @@ -0,0 +1,35 @@ +const config = { + collectCoverageFrom: ['/src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'], + moduleDirectories: ['node_modules'], + moduleFileExtensions: ['js', 'mjs', 'jsx', 'ts', 'tsx', 'json'], + moduleNameMapper: { + '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', + }, + notify: true, + notifyMode: 'success-change', + resetMocks: true, + roots: [''], + setupFilesAfterEnv: ['/jest/setupTests.ts'], + testEnvironment: 'jsdom', + testMatch: [ + '/src/**/*.{spec,test}.{js,jsx,ts,tsx}', + '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', + ], + transform: { + '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': + '/jest/fileTransform.js', + '^.+\\.[jt]sx?$': 'esbuild-jest', + '^.+\\.css$': '/jest/cssTransform.js', + }, + transformIgnorePatterns: [ + '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$', + '^.+\\.module\\.(css|sass|scss)$', + ], + verbose: true, + watchPlugins: [ + 'jest-watch-typeahead/filename', + 'jest-watch-typeahead/testname', + ], +} + +module.exports = config diff --git a/src/Managing.WebApp/mockServiceWorker.js b/src/Managing.WebApp/mockServiceWorker.js new file mode 100644 index 0000000..4e18b44 --- /dev/null +++ b/src/Managing.WebApp/mockServiceWorker.js @@ -0,0 +1,338 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (0.36.5). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = '02f4ad4a2797f85668baf196e553d929' +const bypassHeaderName = 'x-msw-bypass' +const activeClientIds = new Set() + +self.addEventListener('install', function () { + return self.skipWaiting() +}) + +self.addEventListener('activate', async function (event) { + return self.clients.claim() +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll() + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +// Resolve the "main" client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll() + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: serializeHeaders(clonedResponse.headers), + redirected: clonedResponse.redirected, + }, + }) + })() + } + + return response +} + +async function getResponse(event, client, requestId) { + const { request } = event + const requestClone = request.clone() + const getOriginalResponse = () => fetch(requestClone) + + // Bypass mocking when the request client is not active. + if (!client) { + return getOriginalResponse() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return await getOriginalResponse() + } + + // Bypass requests with the explicit bypass header + if (requestClone.headers.get(bypassHeaderName) === 'true') { + const cleanRequestHeaders = serializeHeaders(requestClone.headers) + + // Remove the bypass header to comply with the CORS preflight check. + delete cleanRequestHeaders[bypassHeaderName] + + const originalRequest = new Request(requestClone, { + headers: new Headers(cleanRequestHeaders), + }) + + return fetch(originalRequest) + } + + // Send the request to the client-side MSW. + const reqHeaders = serializeHeaders(request.headers) + const body = await request.text() + + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: reqHeaders, + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body, + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_SUCCESS': { + return delayPromise( + () => respondWithMock(clientMessage), + clientMessage.payload.delay, + ) + } + + case 'MOCK_NOT_FOUND': { + return getOriginalResponse() + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.payload + const networkError = new Error(message) + networkError.name = name + + // Rejecting a request Promise emulates a network error. + throw networkError + } + + case 'INTERNAL_ERROR': { + const parsedBody = JSON.parse(clientMessage.payload.body) + + console.error( + `\ +[MSW] Uncaught exception in the request handler for "%s %s": + +${parsedBody.location} + +This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\ +`, + request.method, + request.url, + ) + + return respondWithMock(clientMessage) + } + } + + return getOriginalResponse() +} + +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = uuidv4() + + return event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) + +function serializeHeaders(headers) { + const reqHeaders = {} + headers.forEach((value, name) => { + reqHeaders[name] = reqHeaders[name] + ? [].concat(reqHeaders[name]).concat(value) + : value + }) + return reqHeaders +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(JSON.stringify(message), [channel.port2]) + }) +} + +function delayPromise(cb, duration) { + return new Promise((resolve) => { + setTimeout(() => resolve(cb()), duration) + }) +} + +function respondWithMock(clientMessage) { + return new Response(clientMessage.payload.body, { + ...clientMessage.payload, + headers: clientMessage.payload.headers, + }) +} + +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = (Math.random() * 16) | 0 + const v = c == 'x' ? r : (r & 0x3) | 0x8 + return v.toString(16) + }) +} diff --git a/src/Managing.WebApp/package.json b/src/Managing.WebApp/package.json new file mode 100644 index 0000000..39f3f07 --- /dev/null +++ b/src/Managing.WebApp/package.json @@ -0,0 +1,102 @@ +{ + "name": "managing", + "version": "2.0.0", + "license": "MIT", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "serve": "serve -s dist -p 3000", + "test": "jest", + "lint": "eslint . --ext .ts,.tsx,.js,jsx", + "lint:fix": "eslint . --ext .ts,.tsx,.js,jsx --fix", + "typecheck": "tsc --noEmit", + "prettier": "prettier --write \"**/*.+(json|yml|css|md|mdx)\"", + "clean": "rimraf node_modules yarn.lock dist", + "validate": "./scripts/validate" + }, + "dependencies": { + "@heroicons/react": "^1.0.6", + "@microsoft/signalr": "^6.0.5", + "@tanstack/react-query": "^4.33.0", + "@wagmi/chains": "^0.2.9", + "@wagmi/core": "^1.3.9", + "@walletconnect/universal-provider": "^2.8.6", + "axios": "^0.27.2", + "classnames": "^2.3.1", + "connectkit": "^1.1.3", + "date-fns": "^2.30.0", + "jotai": "^1.6.7", + "lightweight-charts": "git+https://github.com/ntf/lightweight-charts.git", + "moment": "^2.29.3", + "plotly.js": "^2.18.1", + "react": "^18.1.0", + "react-cookie": "^4.1.1", + "react-dom": "^18.1.0", + "react-grid-layout": "^1.3.4", + "react-hook-form": "^7.31.2", + "react-hot-toast": "^2.2.0", + "react-icons": "^4.3.1", + "react-iframe": "^1.8.0", + "react-plotly.js": "^2.6.0", + "react-router-dom": "^6.3.0", + "react-slider": "^2.0.1", + "react-table": "^7.8.0", + "react-toastify": "^9.0.1", + "reactflow": "^11.8.3", + "viem": "^1.4.2", + "wagmi": "^1.3.10", + "web3": "^4.0.2", + "zustand": "^4.4.1" + }, + "devDependencies": { + "@tailwindcss/aspect-ratio": "^0.4.0", + "@tailwindcss/forms": "^0.5.1", + "@tailwindcss/line-clamp": "^0.4.0", + "@tailwindcss/typography": "^0.5.2", + "@tanstack/eslint-plugin-query": "^4.34.1", + "@testing-library/dom": "^8.13.0", + "@testing-library/react": "^13.2.0", + "@types/jest": "^27.5.1", + "@types/react": "^18.0.9", + "@types/react-dom": "^18.0.4", + "@types/react-grid-layout": "^1.3.2", + "@types/react-plotly.js": "^2.6.0", + "@types/react-slider": "^1.3.1", + "@types/react-table": "^7.7.12", + "@types/signalr": "^2.2.37", + "@types/testing-library__jest-dom": "^5.14.3", + "@typescript-eslint/eslint-plugin": "^5.23.0", + "@typescript-eslint/parser": "^5.23.0", + "@vitejs/plugin-react": "^1.3.2", + "all-contributors-cli": "^6.20.0", + "autoprefixer": "^10.4.7", + "daisyui": "^3.5.1", + "esbuild-jest": "^0.4.0", + "eslint": "^8.15.0", + "eslint-config-prettier": "^8.5.0", + "eslint-config-typescript": "^3.0.0", + "eslint-import-resolver-typescript": "^2.7.1", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react-hooks": "^4.5.0", + "eslint-plugin-sort-keys-fix": "^1.1.2", + "identity-obj-proxy": "^3.0.0", + "jest": "^28.0.0", + "jest-watch-typeahead": "^1.1.0", + "node-notifier": "^10.0.1", + "postcss": "^8.4.13", + "prettier": "^2.6.1", + "prettier-plugin-tailwind-css": "^1.5.0", + "rimraf": "^3.0.2", + "serve": "^14.2.0", + "tailwindcss": "^3.0.23", + "typescript": "^5.0.4", + "vite": "^4.4.9", + "whatwg-fetch": "^3.6.2" + }, + "msw": { + "workerDirectory": "" + } +} diff --git a/src/Managing.WebApp/postcss.config.js b/src/Managing.WebApp/postcss.config.js new file mode 100644 index 0000000..6e41d95 --- /dev/null +++ b/src/Managing.WebApp/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + autoprefixer: {}, + tailwindcss: {}, + }, +} diff --git a/src/Managing.WebApp/prettier.config.js b/src/Managing.WebApp/prettier.config.js new file mode 100644 index 0000000..9280030 --- /dev/null +++ b/src/Managing.WebApp/prettier.config.js @@ -0,0 +1,4 @@ +module.exports = { + plugins: [require('prettier-plugin-tailwindcss')], + tailwindConfig: './tailwind.config.js', +} diff --git a/src/Managing.WebApp/scripts/validate b/src/Managing.WebApp/scripts/validate new file mode 100644 index 0000000..738fceb --- /dev/null +++ b/src/Managing.WebApp/scripts/validate @@ -0,0 +1,11 @@ +#!/bin/sh + +npx concurrently \ + --kill-others-on-fail \ + --prefix "[{name}]" \ + --names "test,lint:fix,typecheck,build" \ + --prefix-colors "bgRed.bold.white,bgGreen.bold.white,bgBlue.bold.white,bgMagenta.bold.white" \ + "npm run test --silent -- --watch=false" \ + "npm run lint:fix --silent" \ + "npm run typecheck --silent" \ + "npm run build --silent" diff --git a/src/Managing.WebApp/src/app/index.tsx b/src/Managing.WebApp/src/app/index.tsx new file mode 100644 index 0000000..96e64a1 --- /dev/null +++ b/src/Managing.WebApp/src/app/index.tsx @@ -0,0 +1,13 @@ +import { Auth } from '../pages/authPage/auth' + +import MyRoutes from './routes' + +const App = () => { + return ( + + + + ) +} + +export default App diff --git a/src/Managing.WebApp/src/app/providers/Hubs.tsx b/src/Managing.WebApp/src/app/providers/Hubs.tsx new file mode 100644 index 0000000..f837f26 --- /dev/null +++ b/src/Managing.WebApp/src/app/providers/Hubs.tsx @@ -0,0 +1,18 @@ +import type { HubConnection } from '@microsoft/signalr' +import { HubConnectionBuilder } from '@microsoft/signalr' +// https://www.abrahamberg.com/blog/aspnet-signalr-and-react/ +export class Hub { + public hub: HubConnection + constructor(name: string, baseUrl: string) { + this.hub = new HubConnectionBuilder() + .withUrl(baseUrl + '/' + name) + .withAutomaticReconnect() + .build() + try { + this.hub.start() + } catch (err) { + // eslint-disable-next-line no-console + console.log(err) + } + } +} diff --git a/src/Managing.WebApp/src/app/routes/index.tsx b/src/Managing.WebApp/src/app/routes/index.tsx new file mode 100644 index 0000000..74bcb4f --- /dev/null +++ b/src/Managing.WebApp/src/app/routes/index.tsx @@ -0,0 +1,118 @@ +import { Suspense, lazy } from 'react' +import { Route, Routes } from 'react-router-dom' + +import LayoutMain from '../../layouts' +import Desk from '../../pages/desk/desk' +import Scenario from '../../pages/scenarioPage/scenario' +import Tools from '../../pages/toolsPage/tools' +import Workflows from '../../pages/workflow/workflows' + +const Backtest = lazy(() => import('../../pages/backtestPage/backtest')) +const Bots = lazy(() => import('../../pages/botsPage/bots')) +const Dashboard = lazy(() => import('../../pages/dashboardPage/dashboard')) +const Settings = lazy(() => import('../../pages/settingsPage/settings')) + +const MyRoutes = () => { + return ( + + }> + + + + } + /> + + + }> + + + + } + /> + + + }> + + + + } + /> + + + }> + + + + } + /> + + + }> + + + + } + /> + + + }> + + + + } + /> + + }> + + + + } + /> + + + }> + + + + } + /> + + {/* }> + + + + } + /> + */} + + ) +} + +export default MyRoutes diff --git a/src/Managing.WebApp/src/app/store/accountStore.tsx b/src/Managing.WebApp/src/app/store/accountStore.tsx new file mode 100644 index 0000000..b264119 --- /dev/null +++ b/src/Managing.WebApp/src/app/store/accountStore.tsx @@ -0,0 +1,22 @@ +import { create } from 'zustand' + +import type { AccountStore } from '../../global/type' + +export const useAuthStore = create((set) => ({ + accounts: [], + onInitialize: () => { + console.log('useFlowStore onInitialize') + + // const accountClient = new AccountClient({}, apiUrl) + // const accounts = await accountClient.account_GetAccounts() + // if (accounts.length > 0) { + // get().setAccounts(accounts) + // } + }, + setAccounts: (accounts) => { + set((state) => ({ + ...state, + accounts: accounts, + })) + }, +})) diff --git a/src/Managing.WebApp/src/app/store/apiStore.tsx b/src/Managing.WebApp/src/app/store/apiStore.tsx new file mode 100644 index 0000000..632fb70 --- /dev/null +++ b/src/Managing.WebApp/src/app/store/apiStore.tsx @@ -0,0 +1,28 @@ +import create from 'zustand' + +type ApiStore = { + isProd: boolean + apiUrl: string + workerUrl: string + toggleApiUrl: () => void +} + +const useApiUrlStore = create((set) => ({ + // Mettez la valeur initiale de isProd ici + apiUrl: import.meta.env.VITE_API_URL_SERVER, + isProd: true, + toggleApiUrl: () => { + set((state) => ({ + apiUrl: state.isProd + ? import.meta.env.VITE_API_URL_LOCAL + : import.meta.env.VITE_API_URL_SERVER, + isProd: !state.isProd, + workerUrl: state.isProd + ? import.meta.env.VITE_WORKER_URL_LOCAL + : import.meta.env.VITE_WORKER_URL_SERVER, + })) + }, + workerUrl: import.meta.env.VITE_WORKER_URL_SERVER, +})) + +export default useApiUrlStore diff --git a/src/Managing.WebApp/src/app/store/flowStore.tsx b/src/Managing.WebApp/src/app/store/flowStore.tsx new file mode 100644 index 0000000..f176552 --- /dev/null +++ b/src/Managing.WebApp/src/app/store/flowStore.tsx @@ -0,0 +1,28 @@ +import { create } from 'zustand' + +import type { IFlow } from '../../generated/ManagingApi' +import { WorkflowClient } from '../../generated/ManagingApi' + +type FlowStore = { + setFlows: (flows: IFlow[]) => void + getFlows: (apiUrl: string) => void + flows: IFlow[] +} + +export const useFlowStore = create((set) => ({ + flows: [] as IFlow[], + getFlows: async (apiUrl) => { + const client = new WorkflowClient({}, apiUrl) + await client.workflow_GetAvailableFlows().then((data) => { + set(() => ({ + flows: data, + })) + }) + }, + setFlows: (flows) => { + set((state) => ({ + ...state, + flows: flows, + })) + }, +})) diff --git a/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx b/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx new file mode 100644 index 0000000..5f8f754 --- /dev/null +++ b/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx @@ -0,0 +1,13 @@ +import type { IWorkflowStore } from '../workflowStore' + +export const WorkflowSelector = (state: IWorkflowStore) => ({ + edges: state.edges, + initWorkFlow: state.initWorkFlow, + nodes: state.nodes, + onConnect: state.onConnect, + onEdgesChange: state.onEdgesChange, + onNodesChange: state.onNodesChange, + resetWorkflow: state.resetWorkflow, + setNodes: state.setNodes, + updateNodeData: state.updateNodeData, +}) diff --git a/src/Managing.WebApp/src/app/store/workflowStore.tsx b/src/Managing.WebApp/src/app/store/workflowStore.tsx new file mode 100644 index 0000000..834b72a --- /dev/null +++ b/src/Managing.WebApp/src/app/store/workflowStore.tsx @@ -0,0 +1,113 @@ +import type { + Connection, + Edge, + EdgeChange, + Node, + NodeChange, + OnNodesChange, + OnEdgesChange, + OnConnect, +} from 'reactflow' +import { addEdge, applyNodeChanges, applyEdgeChanges } from 'reactflow' +import { create } from 'zustand' + +import type { + SyntheticFlowParameter, + FlowParameter, + IFlow, +} from '../../generated/ManagingApi' + +export type IWorkflowStore = { + nodes: Node[] + initialNodes: Node[] + edges: Edge[] + initialEdges: Edge[] + onNodesChange: OnNodesChange + onEdgesChange: OnEdgesChange + onConnect: OnConnect + updateNodeData: (nodeId: string, parameterName: string, value: string) => void + initWorkFlow: (nodes: Node[], edges: Edge[]) => void + setNodes: (nodes: Node[]) => void + resetWorkflow: () => void +} + +// this is our useStore hook that we can use in our components to get parts of the store and call actions +const useWorkflowStore = create((set, get) => ({ + edges: [], + initWorkFlow: (nodes: Node[], edges: Edge[]) => { + set({ + edges: edges, + initialEdges: edges, + initialNodes: nodes, + nodes: nodes, + }) + }, + initialEdges: [], + initialNodes: [], + nodes: [], + onConnect: (connection: Connection, callback: void) => { + set({ + edges: addEdge(connection, get().edges), + }) + }, + onEdgesChange: (changes: EdgeChange[]) => { + set({ + edges: applyEdgeChanges(changes, get().edges), + }) + }, + onNodesChange: (changes: NodeChange[]) => { + set({ + nodes: applyNodeChanges(changes, get().nodes), + }) + }, + resetWorkflow: () => { + set({ + edges: get().initialEdges, + initialEdges: get().initialEdges, + initialNodes: get().initialNodes, + nodes: get().initialNodes, + }) + }, + setNodes: (nodes: Node[]) => { + set({ + nodes: nodes, + }) + }, + updateNodeData: (nodeId: string, parameterName: string, value: string) => { + set({ + nodes: get().nodes.map((node) => { + if (node.id === nodeId) { + node.data.parameters = updateParameters( + node.data.parameters, + parameterName, + value + ) + } + return node + }), + }) + }, +})) + +const updateParameters = ( + parameters: FlowParameter[], + name: string, + value: string +) => { + if (!parameters.find((parameter) => parameter.name === name)) { + parameters.push({ + name: name, + value: value, + } as SyntheticFlowParameter) + } else { + parameters = parameters.map((parameter) => { + if (parameter.name === name) { + parameter.value = value + } + return parameter + }) + } + return parameters +} + +export default useWorkflowStore diff --git a/src/Managing.WebApp/src/assets/img/logo.png b/src/Managing.WebApp/src/assets/img/logo.png new file mode 100644 index 0000000..c4ccd58 Binary files /dev/null and b/src/Managing.WebApp/src/assets/img/logo.png differ diff --git a/src/Managing.WebApp/src/assets/img/tradingview.png b/src/Managing.WebApp/src/assets/img/tradingview.png new file mode 100644 index 0000000..4be6f84 Binary files /dev/null and b/src/Managing.WebApp/src/assets/img/tradingview.png differ diff --git a/src/Managing.WebApp/src/components/atoms/List/List.tsx b/src/Managing.WebApp/src/components/atoms/List/List.tsx new file mode 100644 index 0000000..57c5eac --- /dev/null +++ b/src/Managing.WebApp/src/components/atoms/List/List.tsx @@ -0,0 +1,7 @@ +import type { IListProp } from '../../../global/type' + +const List = (prop: IListProp): JSX.Element => ( +
  • {prop.children}
  • +) + +export default List diff --git a/src/Managing.WebApp/src/components/atoms/Loader/Loader.tsx b/src/Managing.WebApp/src/components/atoms/Loader/Loader.tsx new file mode 100644 index 0000000..3ddfea1 --- /dev/null +++ b/src/Managing.WebApp/src/components/atoms/Loader/Loader.tsx @@ -0,0 +1,16 @@ +import type { ILoader } from '../../../global/type' + +const defaultClasses = 'loading loading-ring ' + +const loaderSize = { + lg: defaultClasses + 'loading-lg', + md: defaultClasses + 'loading-md', + sm: defaultClasses + 'loading-sm', + xs: defaultClasses + 'loading-xs', +} + +const Loader = ({ size = 'md' }: ILoader) => { + return +} + +export default Loader diff --git a/src/Managing.WebApp/src/components/atoms/MyLink/MyLink.tsx b/src/Managing.WebApp/src/components/atoms/MyLink/MyLink.tsx new file mode 100644 index 0000000..c979720 --- /dev/null +++ b/src/Managing.WebApp/src/components/atoms/MyLink/MyLink.tsx @@ -0,0 +1,42 @@ +import { Link, NavLink } from 'react-router-dom' + +import type { IMyLinkProps } from '../../../global/type' + +const MyLink = ({ + children, + href, + as: asof = 'link', + ...props +}: IMyLinkProps): JSX.Element => { + const navLink = asof === 'navlink' + const onlyLink = asof === 'link' + + if (navLink) { + return ( + + {children} + + ) + } + + if (onlyLink) { + return ( + + {children} + + ) + } + + return ( + + {children} + + ) +} + +export default MyLink diff --git a/src/Managing.WebApp/src/components/atoms/Select/Select.tsx b/src/Managing.WebApp/src/components/atoms/Select/Select.tsx new file mode 100644 index 0000000..f31dcf8 --- /dev/null +++ b/src/Managing.WebApp/src/components/atoms/Select/Select.tsx @@ -0,0 +1,7 @@ +import type { IListProp } from '../../../global/type' + +const Select = (prop: IListProp): JSX.Element => ( + +) + +export default Select diff --git a/src/Managing.WebApp/src/components/atoms/Slider/Slider.tsx b/src/Managing.WebApp/src/components/atoms/Slider/Slider.tsx new file mode 100644 index 0000000..a19e1e8 --- /dev/null +++ b/src/Managing.WebApp/src/components/atoms/Slider/Slider.tsx @@ -0,0 +1,30 @@ +import type { FunctionComponent } from 'react' + +import type { IPropsComponent } from '../../../global/type' + +const Slider: FunctionComponent = (props: IPropsComponent) => { + return ( + <> +
    + +
    +
    + {props.prefixValue} + {props.value} + {props.suffixValue} +
    + + ) +} + +export default Slider diff --git a/src/Managing.WebApp/src/components/atoms/index.tsx b/src/Managing.WebApp/src/components/atoms/index.tsx new file mode 100644 index 0000000..77ced26 --- /dev/null +++ b/src/Managing.WebApp/src/components/atoms/index.tsx @@ -0,0 +1,5 @@ +export { default as List } from './List/List' +export { default as MyLink } from './MyLink/MyLink' +export { default as Select } from './Select/Select' +export { default as Slider } from './Slider/Slider' +export { default as Loader } from './Loader/Loader' diff --git a/src/Managing.WebApp/src/components/mollecules/Card/Card.tsx b/src/Managing.WebApp/src/components/mollecules/Card/Card.tsx new file mode 100644 index 0000000..7c5c677 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/Card/Card.tsx @@ -0,0 +1,45 @@ +import type { CardProps } from '../../../global/type' + +const Card = ({ name, children, showCloseButton, info }: CardProps) => { + return ( +
    +
    +
    + {name} + {info && ( +
    + i +
    + )} +
    + +
    + {showCloseButton && ( + + )} +
    +
    +
    {children}
    +
    + ) +} + +export default Card diff --git a/src/Managing.WebApp/src/components/mollecules/CardText/CardText.tsx b/src/Managing.WebApp/src/components/mollecules/CardText/CardText.tsx new file mode 100644 index 0000000..64f51a3 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/CardText/CardText.tsx @@ -0,0 +1,77 @@ +import ArrowDownIcon from '@heroicons/react/solid/ArrowDownIcon' +import ArrowUpIcon from '@heroicons/react/solid/ArrowUpIcon' + +import { Position, TradeDirection } from '../../../generated/ManagingApi' +import type { ICardPosition, ICardSignal, ICardText } from '../../../global/type' + +function getItemTextHeaderClass() { + return 'text-xs ' +} +function getItemTextValueClass() { + return 'text-md ' +} + +export function CardText({ title, content }: ICardText) { + return ( +
    +

    {title}

    +

    {content}

    +
    + ) +} + +export function CardPosition({ positions, positivePosition }: ICardPosition) { + return ( + <> +
    +

    + {positivePosition ? 'Winning position' : 'Losing position'} +

    +

    + { + positions.filter((p: Position) => p.originDirection == TradeDirection.Long) + .length + }{' '} + {' '} + { + positions.filter((p) => p.originDirection == TradeDirection.Short) + .length + }{' '} + +

    +
    + + ) +} + +export function CardSignal({ signals }: ICardSignal) { + return ( + <> +
    +

    Signals

    +

    + {signals.filter((p) => p.direction == TradeDirection.Long).length}{' '} + {' '} + {signals.filter((p) => p.direction == TradeDirection.Short).length}{' '} + +

    +
    + + ) +} diff --git a/src/Managing.WebApp/src/components/mollecules/FormInput/FormInput.tsx b/src/Managing.WebApp/src/components/mollecules/FormInput/FormInput.tsx new file mode 100644 index 0000000..beb18c1 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/FormInput/FormInput.tsx @@ -0,0 +1,24 @@ +import React from 'react' + +import type { IFormInput } from '../../../global/type' + +const FormInput: React.FC = ({ + children, + label, + htmlFor, + inline = false, +}) => { + const groupStyle = inline ? 'flex-wrap' : '' + return ( +
    +
    + + {children} +
    +
    + ) +} + +export default FormInput diff --git a/src/Managing.WebApp/src/components/mollecules/GridTile/GridTile.tsx b/src/Managing.WebApp/src/components/mollecules/GridTile/GridTile.tsx new file mode 100644 index 0000000..a59d89f --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/GridTile/GridTile.tsx @@ -0,0 +1,38 @@ +import type { IGridTile } from '../../../global/type' + +const GridTile = ({ children, title }: IGridTile) => { + return ( +
    +
    +
    + {title} +
    + +
    + +
    +
    +
    {children}
    +
    + ) +} + +export default GridTile diff --git a/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx b/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx new file mode 100644 index 0000000..b80ff7a --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx @@ -0,0 +1,101 @@ +import { StatusOfflineIcon } from '@heroicons/react/solid' +import type { SubmitHandler } from 'react-hook-form' +import { useForm } from 'react-hook-form' +import { useAccount, useDisconnect, useSignMessage } from 'wagmi' + +import useApiUrlStore from '../../../app/store/apiStore' +import { UserClient } from '../../../generated/ManagingApi' +import type { ILoginFormInput } from '../../../global/type' +import useCookie from '../../../hooks/useCookie' +import { SecondaryNavbar } from '../NavBar/NavBar' + +const LogIn = () => { + const { apiUrl } = useApiUrlStore() + const { register, handleSubmit } = useForm() + const { disconnect } = useDisconnect() + const { address } = useAccount() + const { isLoading, signMessageAsync } = useSignMessage({}) + const { setCookie } = useCookie() + + const onSubmit: SubmitHandler = async (form) => { + const message = 'wagmi' + const signature = await signMessageAsync({ message }) + + if (signature && address) { + const userClient = new UserClient({}, apiUrl) + await userClient + .user_CreateToken({ + address: address.toString(), + message: message, + name: form.name, + signature: signature, + }) + .then((data) => { + setCookie('token', data, 1) + location.reload() + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.error(err) + }) + } else { + } + } + + return ( + <> +
    + +
    + +
    +
    +
    +
    +

    + Login +

    +
    +
    + + +
    + + +
    +
    +
    +
    +
    + + ) +} + +export default LogIn diff --git a/src/Managing.WebApp/src/components/mollecules/LogIn/Profile.tsx b/src/Managing.WebApp/src/components/mollecules/LogIn/Profile.tsx new file mode 100644 index 0000000..06f3d42 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/LogIn/Profile.tsx @@ -0,0 +1,26 @@ +import { useConnect } from 'wagmi' + +export function Profile() { + const { connect, connectors, error, isLoading, pendingConnector } = + useConnect() + + return ( +
    + {connectors.map((connector) => ( + + ))} + + {error &&
    {error.message}
    } +
    + ) +} diff --git a/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx b/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx new file mode 100644 index 0000000..752bc3b --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx @@ -0,0 +1,34 @@ +import React from 'react' + +import type { IModalProps } from '../../../global/type' + +import ModalHeader from './ModalHeader' + +const Modal: React.FC = ({ + showModal, + onSubmit, + onClose, + titleHeader, + children, +}) => { + return ( +
    + {showModal ? ( +
    +
    +
    + + {children} +
    +
    +
    + ) : null} +
    + ) +} + +export default Modal diff --git a/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx b/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx new file mode 100644 index 0000000..688ff90 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx @@ -0,0 +1,19 @@ +import React from 'react' + +import type { IModalProps } from '../../../global/type' + +const ModalHeader: React.FC = ({ onClose, titleHeader }: any) => { + return ( +
    + +
    {titleHeader}
    +
    + ) +} + +export default ModalHeader diff --git a/src/Managing.WebApp/src/components/mollecules/NavBar/NavBar.tsx b/src/Managing.WebApp/src/components/mollecules/NavBar/NavBar.tsx new file mode 100644 index 0000000..fd8bc49 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/NavBar/NavBar.tsx @@ -0,0 +1,130 @@ +import { useIsFetching } from '@tanstack/react-query' +import { ConnectKitButton } from 'connectkit' +import type { ReactNode } from 'react' +import { useState } from 'react' +import { Link } from 'react-router-dom' + +import { NavItem } from '..' +import useApiUrlStore from '../../../app/store/apiStore' +import Logo from '../../../assets/img/logo.png' +import { Loader } from '../../atoms' + +const navigation = [ + { href: '/desk', name: 'Desk' }, + { href: '/bots', name: 'Bots' }, + { href: '/workflow', name: 'Workflows' }, + { href: '/scenarios', name: 'Scenarios' }, + { href: '/backtest', name: 'Backtest' }, + { href: '/tools', name: 'Tools' }, + { href: '/settings', name: 'Settings' }, +] + +function navItems(isMobile = false) { + return navigation.map((item) => ( + + {item.name} + + )) +} + +function PrimaryNavbar() { + return ( +
    + + logo + + {/* */} +
    {navItems()}
    +
    + ) +} + +const GlobalLoader = () => { + const isFetching = useIsFetching() + return isFetching ? : null +} + +export function SecondaryNavbar() { + const { toggleApiUrl, isProd } = useApiUrlStore() + + return ( +
    + +
    + +
    + +
    + ) +} + +type MobileMenuButtonProps = { + onClick: VoidFunction +} + +function MobileMenuButton({ onClick }: MobileMenuButtonProps) { + return ( +
    + +
    + ) +} +type MobileMenuProps = { + isOpen: boolean +} + +function MobileMenu({ isOpen }: MobileMenuProps) { + return ( +
    +
      {navItems(true)}
    +
    + ) +} +type NavContainerProps = { + children: ReactNode + isMenuOpen: boolean +} + +function NavContainer({ children, isMenuOpen }: NavContainerProps) { + return ( + + ) +} +export default function NavBar() { + const [isMenuOpen, setIsMenuOpen] = useState(false) + + return ( + + + + setIsMenuOpen(!isMenuOpen)} /> + + ) +} diff --git a/src/Managing.WebApp/src/components/mollecules/NavItem/NavItem.tsx b/src/Managing.WebApp/src/components/mollecules/NavItem/NavItem.tsx new file mode 100644 index 0000000..a492973 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/NavItem/NavItem.tsx @@ -0,0 +1,33 @@ +import { NavLink } from 'react-router-dom' + +import type { INavItemProps } from '../../../global/interface' + +function navLinkClasses(isActive: boolean, isMobile: boolean) { + let commonClasses = 'block text-sm px-2 py-4' + if (isMobile) { + return `${commonClasses} ${ + isActive + ? 'text-base-content bg-primary font-semibold' + : 'hover:bg-primary transition duration-300' + }` + } + commonClasses = + 'py-4 px-2 font-semibold hover:text-primary transition duration-300' + return `${commonClasses} ${isActive ? 'text-primary' : 'text-base-content'}` +} + +export default function NavItem({ + children, + href, + isMobile = false, +}: INavItemProps) { + const item = ( + navLinkClasses(isActive, isMobile)} + > + {children} + + ) + return isMobile ?
  • {item}
  • : item +} diff --git a/src/Managing.WebApp/src/components/mollecules/PieChart/PieChart.tsx b/src/Managing.WebApp/src/components/mollecules/PieChart/PieChart.tsx new file mode 100644 index 0000000..51c6c3b --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/PieChart/PieChart.tsx @@ -0,0 +1,46 @@ +import * as React from 'react' +import Plot from 'react-plotly.js' + +type IPieChart = { + data: number[] + labels: string[] + colors: string[] +} + +const PieChart: React.FC = ({ data, labels, colors }) => { + return ( + <> + + + ) +} + +export default PieChart diff --git a/src/Managing.WebApp/src/components/mollecules/Table/SelectColumnFilter.tsx b/src/Managing.WebApp/src/components/mollecules/Table/SelectColumnFilter.tsx new file mode 100644 index 0000000..f011b60 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/Table/SelectColumnFilter.tsx @@ -0,0 +1,36 @@ +// This is a custom filter UI for selecting + +import React from 'react' + +// a unique option from a list +export default function SelectColumnFilter({ + column: { filterValue, setFilter, preFilteredRows, id }, +}: any) { + // Calculate the options for filtering + // using the preFilteredRows + const options = React.useMemo(() => { + const options = new Set() + preFilteredRows.forEach((row: any) => { + options.add(row.values[id]) + }) + return [...options.values()] + }, [id, preFilteredRows]) + + // Render a multi-select box + return ( + + ) +} diff --git a/src/Managing.WebApp/src/components/mollecules/Table/Table.tsx b/src/Managing.WebApp/src/components/mollecules/Table/Table.tsx new file mode 100644 index 0000000..c562ad9 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/Table/Table.tsx @@ -0,0 +1,231 @@ +import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/solid' +import React from 'react' +import { + useTable, + usePagination, + useSortBy, + useFilters, + useExpanded, +} from 'react-table' + +import type { TableInstanceWithHooks } from '../../../global/type' + +// Define a default UI for filtering +function DefaultColumnFilter({ + column: { filterValue, preFilteredRows, setFilter }, +}: any) { + const count = preFilteredRows.length + + return ( + { + setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely + }} + placeholder={`Search ${count} records...`} + /> + ) +} + +export default function Table({ + columns, + data, + renderRowSubCompontent, + showPagination, + hiddenColumns, + showTotal = false, +}: any) { + const defaultColumn = React.useMemo( + () => ({ + // Let's set up our default Filter UI + Filter: DefaultColumnFilter, + }), + [] + ) + // Use the state and functions returned from useTable to build your UI + const { + getTableProps, + getTableBodyProps, + headerGroups, + prepareRow, + visibleColumns, + page, // Instead of using 'rows', we'll use page, + // which has only the rows for the active page + + // The rest of these things are super handy, too ;) + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + setPageSize, + state: { pageIndex, pageSize }, + } = useTable( + { + columns, + data, + defaultColumn, // Be sure to pass the defaultColumn option + initialState: { + hiddenColumns: hiddenColumns ? hiddenColumns : [], + }, + }, + useFilters, + useSortBy, + useExpanded, + usePagination + ) as TableInstanceWithHooks + + // Calculez le total des valeurs dans la colonne USD + const total = data + ? data + .reduce((sum: number, row: any) => { + return sum + (row.value || 0) // Si la valeur est undefined = 0 + }, 0) + .toFixed(2) + ' $' + : '0.00 $' + + // Render the UI for your table + return ( + <> +
    + + + {headerGroups.map((headerGroup: any) => ( + + {headerGroup.headers.map((column: any) => ( + + ))} + + ))} + + + {page.map((row: any) => { + prepareRow(row) + return ( + <> + + {row.cells.map((cell: any) => { + return ( + + ) + })} + + {row.isExpanded ? ( + + + + ) : null} + + ) + })} + + {/* Afficher le total ici */} + {showTotal ? ( + + + + ) : null} +
    +

    + {column.render('Header')} +

    + + {column.isSorted ? ( + column.isSortedDesc ? ( + + ) : ( + + ) + ) : ( + '' + )} + +
    + {column.canFilter ? column.render('Filter') : null} +
    +
    {cell.render('Cell')}
    + {/* + Inside it, call our renderRowSubComponent function. In reality, + you could pass whatever you want as props to + a component like this, including the entire + table instance. But for this example, we'll just + pass the row + */} + {renderRowSubCompontent({ row })} +
    + Total: {total} +
    +
    + {/* + Pagination can be built however you'd like. + This is just a very basic UI implementation: + */} +
    + {showPagination ? ( +
    + {' '} + {' '} + {' '} + {' '} + + Page{' '} + + {pageIndex + 1} of {pageOptions.length} + {' '} + + {/* + | Go to page:{' '} + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + gotoPage(page) + }} + style={{ width: '100px' }} + /> + {' '} */} + +
    + ) : null} + + ) +} diff --git a/src/Managing.WebApp/src/components/mollecules/Tabs/Tabs.tsx b/src/Managing.WebApp/src/components/mollecules/Tabs/Tabs.tsx new file mode 100644 index 0000000..1f06e88 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/Tabs/Tabs.tsx @@ -0,0 +1,77 @@ +import type { FC } from 'react' + +import type { ITabsProps } from '../../../global/type' + +/** + * Avalible Props + * @param className string + * @param tab Array of object + * @param selectedTab number + * @param onClick Function to set the active tab + * @param orientation Tab orientation Vertical | Horizontal + */ +const Tabs: FC = ({ + className = 'tabs-component', + tabs = [], + selectedTab = 0, + onClick, + orientation = 'horizontal', + addButton = false, + onAddButton, +}) => { + const Panel = tabs && tabs.find((tab) => tab.index === selectedTab) + + return ( +
    +
    + {tabs.map((tab) => ( + + ))} + {addButton && ( + + )} +
    +
    + {Panel && ( + + )} +
    +
    + ) +} +export default Tabs diff --git a/src/Managing.WebApp/src/components/mollecules/ThemeSelector/ThemeSelector.tsx b/src/Managing.WebApp/src/components/mollecules/ThemeSelector/ThemeSelector.tsx new file mode 100644 index 0000000..a526acf --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/ThemeSelector/ThemeSelector.tsx @@ -0,0 +1,21 @@ +import useTheme from '../../../hooks/useTheme' +const themes = ['black', 'coffee', 'cyberpunk', 'lofi', 'retro'] + +const ThemeSelector = (): JSX.Element => { + const { setTheme } = useTheme() + + return ( + + ) +} + +export default ThemeSelector diff --git a/src/Managing.WebApp/src/components/mollecules/Toast/Toast.tsx b/src/Managing.WebApp/src/components/mollecules/Toast/Toast.tsx new file mode 100644 index 0000000..2a6c044 --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/Toast/Toast.tsx @@ -0,0 +1,31 @@ +import type { Id, TypeOptions, UpdateOptions } from 'react-toastify' +import { toast } from 'react-toastify' + +const baseOptions: UpdateOptions = { + autoClose: 5000, + closeOnClick: true, + draggable: true, + hideProgressBar: false, + isLoading: false, + position: 'top-right', + progress: undefined, + theme: 'dark', +} + +class Toast { + private id: Id + + constructor(content: string) { + this.id = toast.loading(content) + } + + update(type: TypeOptions, content: string, opts?: any) { + const options = { ...baseOptions, ...opts } + options.type = type + options.render = content + options.isLoading = false + + toast.update(this.id, options) + } +} +export default Toast diff --git a/src/Managing.WebApp/src/components/mollecules/index.tsx b/src/Managing.WebApp/src/components/mollecules/index.tsx new file mode 100644 index 0000000..15228eb --- /dev/null +++ b/src/Managing.WebApp/src/components/mollecules/index.tsx @@ -0,0 +1,14 @@ +export { CardText, CardPosition, CardSignal } from './CardText/CardText' +export { default as NavItem } from './NavItem/NavItem' +export { default as Tabs } from './Tabs/Tabs' +export { default as Modal } from './Modal/Modal' +export { default as Toast } from './Toast/Toast' +export { default as ThemeSelector } from './ThemeSelector/ThemeSelector' +export { default as Table } from './Table/Table' +export { default as NavBar } from './NavBar/NavBar' +export { default as PieChart } from './PieChart/PieChart' +export { default as FormInput } from './FormInput/FormInput' +export { default as LogIn } from './LogIn/LogIn' +export { default as GridTile } from './GridTile/GridTile' +export { default as SelectColumnFilter } from './Table/SelectColumnFilter' +export { default as Card } from './Card/Card' diff --git a/src/Managing.WebApp/src/components/organism/Account/Account.tsx b/src/Managing.WebApp/src/components/organism/Account/Account.tsx new file mode 100644 index 0000000..0fe15c4 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Account/Account.tsx @@ -0,0 +1,13 @@ +import { useAccount, useEnsName } from 'wagmi' + +export function Account() { + const { address } = useAccount() + const { data: ensName } = useEnsName({ address }) + + return ( +
    + {ensName ?? address?.slice(0, -35)} + {ensName ? ` (${address})` : null} +
    + ) +} diff --git a/src/Managing.WebApp/src/components/organism/ActiveBots/ActiveBots.tsx b/src/Managing.WebApp/src/components/organism/ActiveBots/ActiveBots.tsx new file mode 100644 index 0000000..4814f8a --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/ActiveBots/ActiveBots.tsx @@ -0,0 +1,211 @@ +import { + ArrowDownIcon, + ArrowUpIcon, + ChevronDownIcon, + ChevronRightIcon, + PlayIcon, +} from '@heroicons/react/solid' +import React, { useEffect, useState } from 'react' + +import { Hub } from '../../../app/providers/Hubs' +import useApiUrlStore from '../../../app/store/apiStore' +import type { Account, TradingBot } from '../../../generated/ManagingApi' +import { + AccountClient, + BotClient, + TradeDirection, + TradeStatus, +} from '../../../generated/ManagingApi' +import { SelectColumnFilter, Table } from '../../mollecules' +import BacktestRowDetails from '../Backtest/backtestRowDetails' +import StatusBadge from '../StatusBadge/StatusBadge' +import Summary from '../Trading/Summary' + +export default function ActiveBots() { + const [bots, setBots] = useState([]) + const [accounts, setAccounts] = useState([]) + const { apiUrl } = useApiUrlStore() + + const columns = React.useMemo( + () => [ + { + Cell: ({ row }: any) => ( + // Use Cell to render an expander for each row. + // We can use the getToggleRowExpandedProps prop-getter + // to build the expander. + + {row.isExpanded ? ( + + ) : ( + + )} + + ), + + // Make sure it has an ID + Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }: any) => ( + + {isAllRowsExpanded ? 'v' : '>'} + + ), + // Build our expander column + id: 'expander', + }, + { + Cell: ({ cell }: any) => ( + <> + + + ), + Header: 'Status', + accessor: 'status', + disableFilters: true, + sortType: 'basic', + }, + { + accessor: 'isForWatchingOnly', + }, + { + Filter: SelectColumnFilter, + Header: 'Ticker', + accessor: 'ticker', + disableSortBy: true, + }, + { + Header: 'Account', + accessor: 'accountName', + }, + { + Filter: SelectColumnFilter, + Header: 'Timeframe', + accessor: 'timeframe', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Scenario', + accessor: 'scenario', + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <> + { + <> + { + cell.row.values.positions.filter( + (p: any) => p.originDirection == TradeDirection.Long + ).length + }{' '} + + {' | '} + { + cell.row.values.positions.filter( + (p: any) => p.originDirection == TradeDirection.Short + ).length + }{' '} + {' '} + { + cell.row.values.positions.filter( + (p: any) => p.status == TradeStatus.Filled + ).length + }{' '} + {' '} + + } + + ), + Header: 'Positions', + accessor: 'positions', + disableFilters: true, + }, + { + Cell: ({ cell }) => <>{cell.row.values.winRate} %, + Header: 'Winrate', + accessor: 'winRate', + disableFilters: true, + }, + { + Cell: ({ cell }) => <>{cell.row.values.profitAndLoss} $, + Header: 'PNL', + accessor: 'profitAndLoss', + disableFilters: true, + }, + ], + [] + ) + + useEffect(() => { + setupHubConnection().then(() => { + if (bots.length == 0) { + const client = new BotClient({}, apiUrl) + client.bot_GetActiveBots().then((data) => { + setBots(data) + }) + } + }) + const client = new AccountClient({}, apiUrl) + client.account_GetAccounts().then((data) => { + setAccounts(data) + }) + }, []) + + const setupHubConnection = async () => { + const hub = new Hub('bothub', apiUrl).hub + + hub.on('BotsSubscription', (data: TradingBot[]) => { + // eslint-disable-next-line no-console + console.log( + 'bot List', + bots.map((bot) => { + return bot.name + }) + ) + setBots(data) + }) + + return hub + } + + const renderRowSubComponent = React.useCallback( + ({ row }: any) => ( + <> + + + ), + [] + ) + + return ( + <> +
    + +
    +
    + + + + ) +} diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx new file mode 100644 index 0000000..4bb97e0 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx @@ -0,0 +1,309 @@ +import { DotsVerticalIcon, TrashIcon } from '@heroicons/react/solid' +import moment from 'moment' +import React from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import type { + Backtest, + MoneyManagement, + StartBotRequest, + Ticker, +} from '../../../generated/ManagingApi' +import { + BacktestClient, + BotClient, + BotType, +} from '../../../generated/ManagingApi' +import type { IBacktestCards } from '../../../global/type' +import MoneyManagementModal from '../../../pages/settingsPage/moneymanagement/moneyManagementModal' +import { CardPosition, CardText, Toast } from '../../mollecules' +import CardPositionItem from '../Trading/CardPositionItem' +import TradeChart from '../Trading/TradeChart/TradeChart' + +function baseBadgeClass(isOutlined = false) { + let classes = 'text-xs badge ' + + if (isOutlined) { + classes += 'badge-outline ' + } + return classes +} + +function botStatusResult( + growthPercentage: number | undefined, + hodlPercentage: number | undefined +) { + if (growthPercentage != undefined && hodlPercentage != undefined) { + const isWinning = growthPercentage > hodlPercentage + const classes = + baseBadgeClass() + (isWinning ? 'badge-success' : 'badge-content') + return
    {isWinning ? 'Winning' : 'Losing'}
    + } +} + +// function that return the number of day between a date and today +function daysBetween(date: Date) { + const oneDay = 24 * 60 * 60 * 1000 // hours*minutes*seconds*milliseconds + const firstDate = new Date(date) + const secondDate = new Date() + const diffDays = Math.round( + Math.abs((firstDate.getTime() - secondDate.getTime()) / oneDay) + ) + return diffDays +} + +const BacktestCards: React.FC = ({ list, setBacktests }) => { + const { apiUrl } = useApiUrlStore() + const [showMoneyManagementModal, setShowMoneyManagementModal] = + React.useState(false) + const [selectedMoneyManagement, setSelectedMoneyManagement] = + React.useState() + + async function runBot(backtest: Backtest, isForWatchOnly: boolean) { + const t = new Toast('Bot is starting') + const client = new BotClient({}, apiUrl) + + const request: StartBotRequest = { + accountName: backtest.accountName, + botName: backtest.ticker + '-' + backtest.timeframe.toString(), + botType: BotType.ScalpingBot, + isForWatchOnly: isForWatchOnly, + moneyManagementName: backtest.moneyManagement?.name, + scenario: backtest.scenario, + ticker: backtest.ticker as Ticker, + timeframe: backtest.timeframe, + } + + await client + .bot_Start(request) + .then((botStatus: string) => { + t.update('info', 'Bot status :' + botStatus) + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + + async function runOptimizedBacktest(backtest: Backtest) { + const t = new Toast('Optimized backtest is running') + const client = new BacktestClient({}, apiUrl) + + await client + .backtest_Run( + backtest.accountName, + backtest.botType, + backtest.ticker as Ticker, + backtest.scenario, + backtest.timeframe, + false, + daysBetween(backtest.candles[0].date), + backtest.walletBalances[0].value, + '', + false, + backtest.optimizedMoneyManagement + ) + .then((backtest: Backtest) => { + t.update('success', `${backtest.ticker} Backtest Succeeded`) + setBacktests((arr) => [...arr, backtest]) + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + + function saveMoneyManagement(moneyManagement: MoneyManagement) { + setSelectedMoneyManagement(moneyManagement) + setShowMoneyManagementModal(true) + } + + return ( +
    + {list.map((backtest: Backtest, index) => ( +
    +
    +
    + +
    + +
    +
    + { + + } +
    + +
    +

    +
    + +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + {backtest.ticker} + {botStatusResult( + backtest.growthPercentage, + backtest.hodlPercentage + )} +

    +
    +
    + + + + +
    +
    +
    + + {/* */} + { + const realized = p.profitAndLoss?.realized ?? 0 + return realized > 0 ? p : null + })} + > + { + const realized = p.profitAndLoss?.realized ?? 0 + return realized <= 0 ? p : null + })} + > + +
    + +
    +
    + + + + +
    +
    +
    +
    + WR {backtest.winRate?.toFixed(2).toString()} % +
    +
    + PNL {backtest.growthPercentage?.toFixed(2).toString()} % +
    +
    +
    +
    +
    +
    + ))} + + setShowMoneyManagementModal(false)} + /> +
    + ) +} + +export default BacktestCards diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestModal.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestModal.tsx new file mode 100644 index 0000000..f8c2f75 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Backtest/backtestModal.tsx @@ -0,0 +1,361 @@ +import { useQuery } from '@tanstack/react-query' +import React, { useEffect, useState } from 'react' +import { useForm, type SubmitHandler } from 'react-hook-form' + +import useApiUrlStore from '../../../app/store/apiStore' +import type { + Backtest, + MoneyManagement, + Ticker, +} from '../../../generated/ManagingApi' +import { + AccountClient, + BacktestClient, + BotType, + DataClient, + MoneyManagementClient, + ScenarioClient, + Timeframe, +} from '../../../generated/ManagingApi' +import type { + BacktestModalProps, + IBacktestsFormInput, +} from '../../../global/type' +import { Loader, Slider } from '../../atoms' +import { Modal, Toast } from '../../mollecules' +import FormInput from '../../mollecules/FormInput/FormInput' +import CustomMoneyManagement from '../CustomMoneyManagement/CustomMoneyManagement' + +const BacktestModal: React.FC = ({ + showModal, + closeModal, + setBacktests, + showLoopSlider = false, +}) => { + const [selectedAccount, setSelectedAccount] = React.useState() + const [selectedTimeframe, setSelectedTimeframe] = React.useState() + const [selectedLoopQuantity, setLoopQuantity] = React.useState( + showLoopSlider ? 3 : 1 + ) + const [balance, setBalance] = React.useState(10000) + const [days, setDays] = React.useState(-10) + + const [customMoneyManagement, setCustomMoneyManagement] = + React.useState() + const [selectedMoneyManagement, setSelectedMoneyManagement] = + useState() + const [showCustomMoneyManagement, setShowCustomMoneyManagement] = + useState(false) + + const { apiUrl } = useApiUrlStore() + + const scenarioClient = new ScenarioClient({}, apiUrl) + const accountClient = new AccountClient({}, apiUrl) + const dataClient = new DataClient({}, apiUrl) + const moneyManagementClient = new MoneyManagementClient({}, apiUrl) + const backtestClient = new BacktestClient({}, apiUrl) + + const { register, handleSubmit } = useForm() + const onSubmit: SubmitHandler = async (form) => { + const { scenarioName, tickers } = form + closeModal() + for (let sIndex = 0; sIndex < scenarioName.length; sIndex++) { + for (let tIndex = 0; tIndex < tickers.length; tIndex++) { + await runBacktest( + form, + form.tickers[tIndex], + form.scenarioName[sIndex], + customMoneyManagement, + 1 + ) + } + } + } + + async function runBacktest( + form: IBacktestsFormInput, + ticker: string, + scenarioName: string, + moneyManagement: MoneyManagement | undefined, + loopCount: number + ) { + const t = new Toast(ticker + ' is running') + await backtestClient + .backtest_Run( + form.accountName, + form.botType, + ticker as Ticker, + scenarioName, + form.timeframe, + false, + days, + balance, + selectedMoneyManagement, + form.save, + selectedMoneyManagement ? undefined : moneyManagement + ) + .then((backtest: Backtest) => { + t.update('success', `${backtest.ticker} Backtest Succeeded`) + setBacktests((arr) => [...arr, backtest]) + + if (showLoopSlider && selectedLoopQuantity > loopCount) { + const nextCount = loopCount + 1 + const mm: MoneyManagement = { + balanceAtRisk: backtest.optimizedMoneyManagement.balanceAtRisk, + leverage: backtest.optimizedMoneyManagement.leverage, + name: backtest.optimizedMoneyManagement.name + nextCount, + stopLoss: backtest.optimizedMoneyManagement.stopLoss, + takeProfit: backtest.optimizedMoneyManagement.takeProfit, + timeframe: backtest.optimizedMoneyManagement.timeframe, + } + runBacktest(form, ticker, scenarioName, mm, nextCount) + } + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + + function setSelectedAccountEvent(e: React.ChangeEvent) { + setSelectedAccount(e.target.value) + } + + function setSelectedTimeframeEvent(e: any) { + setSelectedTimeframe(e.target.value) + } + + function onMoneyManagementChange(e: any) { + if (e.target.value == 'Custom') { + setShowCustomMoneyManagement(true) + setSelectedMoneyManagement(e.target.value) + } else { + setShowCustomMoneyManagement(false) + setCustomMoneyManagement(undefined) + setSelectedMoneyManagement(undefined) + } + } + + const { data: scenarios } = useQuery({ + queryFn: () => scenarioClient.scenario_GetScenarios(), + queryKey: ['scenarios'], + }) + + const { data: accounts } = useQuery({ + onSuccess: () => { + if (accounts) { + setSelectedAccount(accounts[0].name) + } + setSelectedTimeframe(Timeframe.FiveMinutes) + }, + queryFn: () => accountClient.account_GetAccounts(), + queryKey: ['accounts'], + }) + + const { data: tickers, refetch: refetchTickers } = useQuery({ + enabled: !!selectedAccount && !!selectedTimeframe, + queryFn: () => { + if (selectedAccount && selectedTimeframe) { + return dataClient.data_GetTickers(selectedAccount, selectedTimeframe) + } + }, + queryKey: ['tickers', selectedAccount, selectedTimeframe], + }) + + const { data: moneyManagements } = useQuery({ + enabled: !!selectedTimeframe, + onSuccess: (data) => { + if (data) { + setSelectedMoneyManagement(data[0].name) + } + }, + queryFn: async () => { + if (selectedTimeframe) { + const mm = + await moneyManagementClient.moneyManagement_GetMoneyManagements() + mm.push({ + balanceAtRisk: 1, + leverage: 1, + name: 'Custom', + stopLoss: 1, + takeProfit: 1, + timeframe: selectedTimeframe, + }) + return mm + } + }, + queryKey: ['moneyManagements', selectedTimeframe], + }) + + useEffect(() => { + if (selectedAccount && selectedTimeframe) { + refetchTickers() + } + }, [selectedAccount, selectedTimeframe]) + + if (!accounts || !scenarios || !moneyManagements) { + return + } + + return ( + + + + + + + + + + + + + + + + + + + + setDays(e.target.value)} + step="1" + min="-360" + max="-1" + > + + + + setBalance(e.target.value)} + step="1000" + min="1000" + max="100000" + > + + + + + + + + + + + {/* Loop Quantity */} + {showLoopSlider ? ( + + setLoopQuantity(e.target.value)} + step="1" + min="1" + max="20" + > + + ) : null} + +
    + +
    + +
    + +
    +
    + ) +} +export default BacktestModal diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx new file mode 100644 index 0000000..1ce93dd --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx @@ -0,0 +1,47 @@ +import { TradeChart, CardPositionItem } from '..' +import type { IBotRowDetails } from '../../../global/interface' +import { CardPosition } from '../../mollecules' + +const BacktestRowDetails: React.FC = ({ + candles, + positions, + walletBalances, +}) => { + return ( + <> +
    +
    + { + const realized = p.profitAndLoss?.realized ?? 0 + return realized > 0 ? p : null + })} + > + { + const realized = p.profitAndLoss?.realized ?? 0 + return realized <= 0 ? p : null + })} + > + +
    +
    +
    + +
    +
    +
    + + ) +} + +export default BacktestRowDetails diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestTable.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestTable.tsx new file mode 100644 index 0000000..4289a33 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Backtest/backtestTable.tsx @@ -0,0 +1,275 @@ +import { + ChevronDownIcon, + ChevronRightIcon, + PlayIcon, + TrashIcon, +} from '@heroicons/react/solid' +import React, { useEffect, useState } from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import type { + Backtest, + StartBotRequest, + Ticker, +} from '../../../generated/ManagingApi' +import { BacktestClient, BotClient } from '../../../generated/ManagingApi' +import type { IBacktestCards } from '../../../global/type' +import { Toast, SelectColumnFilter, Table } from '../../mollecules' + +import BacktestRowDetails from './backtestRowDetails' + +const BacktestTable: React.FC = ({ list, isFetching }) => { + const [rows, setRows] = useState([]) + const { apiUrl } = useApiUrlStore() + + async function runBot(backtest: Backtest, isForWatchOnly: boolean) { + const t = new Toast('Bot is starting') + const client = new BotClient({}, apiUrl) + + const request: StartBotRequest = { + accountName: backtest.accountName, + botName: backtest.ticker + '-' + backtest.timeframe.toString(), + botType: backtest.botType, + isForWatchOnly: isForWatchOnly, + moneyManagementName: '', + scenario: backtest.scenario, + ticker: backtest.ticker as Ticker, + timeframe: backtest.timeframe, + } + + await client + .bot_Start(request) + .then((botStatus: string) => { + t.update('info', 'Bot status :' + botStatus) + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + + async function deleteBacktest(id: string) { + const t = new Toast('Deleting backtest') + const client = new BacktestClient({}, apiUrl) + + await client + .backtest_DeleteBacktest(id) + .then(() => { + t.update('success', 'Backtest deleted') + }) + .catch((err) => { + t.update('error', err) + }) + } + + const columns = React.useMemo( + () => [ + { + Header: 'Informations', + columns: [ + { + Cell: ({ row }: any) => ( + // Use Cell to render an expander for each row. + // We can use the getToggleRowExpandedProps prop-getter + // to build the expander. + + {row.isExpanded ? ( + + ) : ( + + )} + + ), + + // Make sure it has an ID + Header: ({ + getToggleAllRowsExpandedProps, + isAllRowsExpanded, + }: any) => ( + + {isAllRowsExpanded ? 'v' : '>'} + + ), + // Build our expander column + id: 'expander', + }, + { + Filter: SelectColumnFilter, + Header: 'Ticker', + accessor: 'ticker', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Timeframe', + accessor: 'timeframe', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Scenario', + accessor: 'scenario', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'BotType', + accessor: 'botType', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Account', + accessor: 'accountName', + disableSortBy: true, + }, + ], + }, + { + Header: 'Results', + columns: [ + { + Cell: ({ cell }: any) => ( + <>{cell.row.values.finalPnl.toFixed(2)} $ + ), + Header: 'Pnl $', + accessor: 'finalPnl', + disableFilters: true, + sortType: 'basic', + }, + { + Cell: ({ cell }: any) => ( + <>{cell.row.values.hodlPercentage.toFixed(2)} % + ), + Header: 'Hodl %', + accessor: 'hodlPercentage', + disableFilters: true, + sortType: 'basic', + }, + { + Cell: ({ cell }: any) => <>{cell.row.values.winRate} %, + Header: 'Winrate', + accessor: 'winRate', + disableFilters: true, + }, + { + Cell: ({ cell }: any) => ( + <>{cell.row.values.growthPercentage.toFixed(2)} % + ), + Header: 'Pnl %', + accessor: 'growthPercentage', + disableFilters: true, + sortType: 'basic', + }, + { + Cell: ({ cell }: any) => ( + <> + {( + cell.row.values.growthPercentage - + cell.row.values.hodlPercentage + ).toFixed(2)} + + ), + Header: 'H/P', + accessor: 'diff', + disableFilters: true, + sortType: 'basic', + }, + ], + }, + { + Header: 'Action', + columns: [ + { + Cell: ({ cell }: any) => ( + <> +
    + +
    + + ), + Header: '', + accessor: 'id', + disableFilters: true, + }, + // { + // Cell: ({ cell }) => ( + // <> + //
    + // + //
    + // + // ), + // Header: '', + // accessor: 'watcher', + // disableFilters: true, + // }, + { + Cell: ({ cell }: any) => ( + <> +
    + +
    + + ), + Header: '', + accessor: 'bot', + disableFilters: true, + }, + ], + }, + ], + [] + ) + + useEffect(() => { + setRows(list) + }, [list]) + + const renderRowSubComponent = React.useCallback( + ({ row }: any) => ( + <> + + + ), + [] + ) + + return ( +
    + {isFetching ? ( + + ) : ( +
    + )} + + ) +} + +export default BacktestTable diff --git a/src/Managing.WebApp/src/components/organism/CustomMoneyManagement/CustomMoneyManagement.tsx b/src/Managing.WebApp/src/components/organism/CustomMoneyManagement/CustomMoneyManagement.tsx new file mode 100644 index 0000000..78f3573 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/CustomMoneyManagement/CustomMoneyManagement.tsx @@ -0,0 +1,104 @@ +import React, { useEffect, useState } from 'react' + +import type { MoneyManagement, Timeframe } from '../../../generated/ManagingApi' +import { Slider } from '../../atoms' +import FormInput from '../../mollecules/FormInput/FormInput' + +type ICustomMoneyManagement = { + onCreateMoneyManagement: (moneyManagement: MoneyManagement) => void + timeframe: Timeframe + showCustomMoneyManagement: boolean +} + +const CustomMoneyManagement: React.FC = ({ + onCreateMoneyManagement, + timeframe, + showCustomMoneyManagement, +}) => { + const [balanceAtRisk, setBalanceAtRisk] = useState(1) + const [leverage, setLeverage] = useState(1) + const [takeProfit, setTakeProfit] = useState(1) + const [stopLoss, setStopLoss] = useState(1) + + const handleCreateMoneyManagement = () => { + const moneyManagement: MoneyManagement = { + balanceAtRisk, + leverage, + name: 'custom', + stopLoss, + takeProfit, + timeframe, + } + onCreateMoneyManagement(moneyManagement) + } + + useEffect(() => { + handleCreateMoneyManagement() + }, [balanceAtRisk, leverage, takeProfit, stopLoss]) + + return ( + <> + {showCustomMoneyManagement ? ( +
    + +
    + Custom MoneyManagement +
    +
    + + setBalanceAtRisk(parseInt(e.target.value))} + min="1" + max="100" + step="1" + suffixValue=" %" + > + + + + setLeverage(e.target.value)} + prefixValue="x " + > + + + + setTakeProfit(e.target.value)} + step="0.01" + max="20" + suffixValue=" %" + > + + + + setStopLoss(e.target.value)} + step="0.01" + max="20" + suffixValue=" %" + > + +
    +
    + ) : null} + + ) +} + +export default CustomMoneyManagement diff --git a/src/Managing.WebApp/src/components/organism/Positions/PositionList.tsx b/src/Managing.WebApp/src/components/organism/Positions/PositionList.tsx new file mode 100644 index 0000000..af7231d --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Positions/PositionList.tsx @@ -0,0 +1,128 @@ +import { StopIcon } from '@heroicons/react/solid' +import moment from 'moment' +import React from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import { + TradeDirection, + type Position, + Ticker, + PositionStatus, + TradingClient, +} from '../../../generated/ManagingApi' +import { Toast, Table } from '../../mollecules' + +import PositionStatusBadge from './PositionStatusBadge' + +type IPositionList = { + positions: Position[] + isFetching: boolean +} + +const PositionsList: React.FC = ({ positions, isFetching }) => { + const { apiUrl } = useApiUrlStore() + + async function closePosition(identifier: string) { + const t = new Toast('Closing position') + const client = new TradingClient({}, apiUrl) + await client + .trading_ClosePosition(identifier) + .then(() => { + t.update('success', 'Position closed') + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + + const columns = React.useMemo( + () => [ + { + Cell: ({ cell }: any) => ( + <> +
    + +
    + + ), + Header: 'Status', + accessor: 'status', + disableFilters: true, + sortType: 'basic', + }, + + { + Cell: ({ cell }: any) =>
    {Object.values(Ticker)[cell.value]}
    , + Header: 'Ticker', + accessor: 'ticker', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( +
    {Object.values(TradeDirection)[cell.value]}
    + ), + Header: 'Direction', + accessor: 'originDirection', + disableFilters: true, + disableSortBy: true, + }, + { + Header: 'Account', + accessor: 'accountName', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => moment(cell.value).fromNow(), + Header: 'Date', + accessor: 'date', + disableFilters: true, + }, + { + Cell: ({ cell }: any) => ( +
    {(cell.value.realized as number).toFixed(4)} $
    + ), + Header: 'uPNL', + accessor: 'profitAndLoss', + disableFilters: true, + }, + { + Cell: ({ cell }) => ( + <> +
    + +
    + + ), + Header: 'Actions', + accessor: 'identifier', + disableFilters: true, + }, + ], + [] + ) + return ( +
    + {isFetching ? ( +
    + +
    + ) : ( +
    + )} + + ) +} + +export default PositionsList diff --git a/src/Managing.WebApp/src/components/organism/Positions/PositionStatusBadge.tsx b/src/Managing.WebApp/src/components/organism/Positions/PositionStatusBadge.tsx new file mode 100644 index 0000000..2ac4f64 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Positions/PositionStatusBadge.tsx @@ -0,0 +1,30 @@ +import { PositionStatus } from '../../../generated/ManagingApi' + +type IPositionStatusBadge = { + status: PositionStatus +} +function statusClasses(status: PositionStatus) { + let commonClasses = 'badge badge-xs ' + switch (status) { + case PositionStatus.Canceled: + case PositionStatus.Rejected: + commonClasses += 'bg-red-100' + break + case PositionStatus.New: + commonClasses += 'bg-blue-100' + break + case PositionStatus.PartiallyFilled: + commonClasses += 'bg-orange-100' + break + case PositionStatus.Filled: + commonClasses += 'bg-green-100' + break + default: + break + } + return commonClasses +} + +export default function PositionStatusBadge({ status }: IPositionStatusBadge) { + return +} diff --git a/src/Managing.WebApp/src/components/organism/SpotLightBadge/SpotLightBadge.tsx b/src/Managing.WebApp/src/components/organism/SpotLightBadge/SpotLightBadge.tsx new file mode 100644 index 0000000..c1e260a --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/SpotLightBadge/SpotLightBadge.tsx @@ -0,0 +1,31 @@ +import moment from 'moment' + +import { TradeDirection } from '../../../generated/ManagingApi' +import type { ISpotlightBadge } from '../../../global/type' + +function GetBadgeColor(direction: TradeDirection | undefined) { + switch (direction) { + case TradeDirection.Long: + return 'badge bg-success' + case TradeDirection.Short: + return 'badge bg-error' + case TradeDirection.None: + return 'badge bg-warning' + default: + return 'badge' + } +} + +export default function SpotLightBadge({ + direction, + date, + price, +}: ISpotlightBadge) { + const tooltipText = + date == undefined ? 'No signal' : moment(date).fromNow() + ' @ ' + price + return ( +
    +
    +
    + ) +} diff --git a/src/Managing.WebApp/src/components/organism/StatusBadge/StatusBadge.tsx b/src/Managing.WebApp/src/components/organism/StatusBadge/StatusBadge.tsx new file mode 100644 index 0000000..4c44184 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/StatusBadge/StatusBadge.tsx @@ -0,0 +1,14 @@ +import type { IStatusBadge } from '../../../global/type' + +function statusClasses(status: string, isForWatchOnly: boolean) { + const commonClasses = 'badge badge-xs' + if (isForWatchOnly) { + return `${commonClasses} 'bg-blue-500'` + } + + return `${commonClasses} ${status == 'Up' ? 'bg-green-500' : 'bg-red-500'}` +} + +export default function StatusBadge({ status, isForWatchOnly }: IStatusBadge) { + return +} diff --git a/src/Managing.WebApp/src/components/organism/Trading/CardPositionItem.tsx b/src/Managing.WebApp/src/components/organism/Trading/CardPositionItem.tsx new file mode 100644 index 0000000..8a8e192 --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Trading/CardPositionItem.tsx @@ -0,0 +1,34 @@ +import { PositionStatus } from '../../../generated/ManagingApi' +import type { ICardPositionFlipped } from '../../../global/type' +import { CardText } from '../../mollecules' + +const CardPositionFlipped: React.FC = ({ positions }) => { + return ( + <> + { + const realized = p.profitAndLoss?.realized ?? 0 + return realized > 0 && p.status == PositionStatus.Flipped + ? p + : null + }) + .length.toString() + + ' | ' + + positions + .filter((p) => { + const realized = p.profitAndLoss?.realized ?? 0 + return realized <= 0 && p.status == PositionStatus.Flipped + ? p + : null + }) + .length.toString() + } + > + + ) +} + +export default CardPositionFlipped diff --git a/src/Managing.WebApp/src/components/organism/Trading/Summary.tsx b/src/Managing.WebApp/src/components/organism/Trading/Summary.tsx new file mode 100644 index 0000000..021531d --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Trading/Summary.tsx @@ -0,0 +1,133 @@ +import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/solid' +import React, { useEffect, useState } from 'react' + +import type { TradingBot } from '../../../generated/ManagingApi' +import { PositionStatus, TradeDirection } from '../../../generated/ManagingApi' +import type { IAccountBalanceProps } from '../../../global/type' + +function GetGlobalWinrate(bots: TradingBot[]) { + if (bots == null || bots == undefined || bots.length == 0) { + return 0 + } + + let totalPositions = 0 + let winningPosition = 0 + + bots.forEach((bot) => { + totalPositions += bot.positions.filter( + (p) => p.status != PositionStatus.New + ).length + winningPosition += bot.positions.filter((p) => { + const realized = p.profitAndLoss?.realized ?? 0 + return realized > 0 && + (p.status == PositionStatus.Finished || + p.status == PositionStatus.Flipped) + ? p + : null + }).length + }) + + if (totalPositions == 0) return 0 + + return (winningPosition * 100) / totalPositions +} + +function GetPositionCount( + bots: TradingBot[], + direction: TradeDirection, + status: PositionStatus +) { + let totalPositions = 0 + + if (bots == null || bots == undefined) { + return 0 + } + + bots.forEach((bot) => { + totalPositions += bot.positions.filter( + (p) => p.status == status && p.originDirection == direction + ).length + }) + + return totalPositions +} + +const Summary: React.FC = ({ bots }) => { + const [globalPnl, setGlobalPnl] = useState(0) + const [globalWinrate, setGlobalWinrate] = useState(0) + + const [openLong, setLong] = useState(0) + const [openShort, setShort] = useState(0) + + const [closedLong, setClosedLong] = useState(0) + const [closedShort, setClosedShort] = useState(0) + + useEffect(() => { + if (bots) { + const pnl = bots.reduce((acc, bot) => { + return acc + bot.profitAndLoss + }, 0) + setGlobalPnl(pnl) + setGlobalWinrate(GetGlobalWinrate(bots)) + setLong( + GetPositionCount(bots, TradeDirection.Long, PositionStatus.Filled) + ) + setShort( + GetPositionCount(bots, TradeDirection.Short, PositionStatus.Filled) + ) + setClosedLong( + GetPositionCount(bots, TradeDirection.Long, PositionStatus.Finished) + + GetPositionCount(bots, TradeDirection.Long, PositionStatus.Flipped) + ) + setClosedShort( + GetPositionCount(bots, TradeDirection.Short, PositionStatus.Finished) + + GetPositionCount(bots, TradeDirection.Short, PositionStatus.Flipped) + ) + } + }, [bots]) + + return ( +
    +
    + +
    +
    +
    Bots running
    +
    {bots.length}
    +
    + +
    +
    Total Profit
    +
    {globalPnl.toFixed(4)} $
    +
    + +
    +
    Global Winrate
    +
    + {globalWinrate ? globalWinrate.toFixed(2) : 0} % +
    +
    + +
    +
    Positions Openend
    +
    + {openLong} {' '} + {openShort}{' '} + {' '} +
    +
    +
    +
    Positions Closed
    +
    + {closedLong}{' '} + {' '} + {closedShort}{' '} + {' '} +
    +
    +
    +
    + ) +} + +export default Summary diff --git a/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx b/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx new file mode 100644 index 0000000..60d4daa --- /dev/null +++ b/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx @@ -0,0 +1,302 @@ +import type { + CandlestickData, + IChartApi, + ISeriesApi, + PriceLineOptions, + SeriesMarker, + SeriesMarkerShape, + Time, + UTCTimestamp, +} from 'lightweight-charts' +import { LineStyle, createChart, CrosshairMode } from 'lightweight-charts' +import moment from 'moment' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' + +import type { + Candle, + KeyValuePairOfDateTimeAndDecimal, + Position, + Signal, +} from '../../../../generated/ManagingApi' +import { + PositionStatus, + TradeDirection, +} from '../../../../generated/ManagingApi' +import useTheme from '../../../../hooks/useTheme' + +type ITradeChartProps = { + candles: Candle[] + positions: Position[] + signals: Signal[] + walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null + stream?: Candle | null + width: number + height: number +} + +const TradeChart = ({ + candles, + positions, + signals, + walletBalances, + stream, + width, + height, +}: ITradeChartProps) => { + const chartRef = React.useRef(null) + const chart = useRef() + const { themeProperty } = useTheme() + const theme = themeProperty() + const series1 = useRef>() + const [timeDiff, setTimeDiff] = useState(0) + const [candleCount, setCandleCount] = useState(candles.length) + + function buildLine( + color: string, + price: number, + title: string + ): PriceLineOptions { + return { + axisLabelVisible: true, + color: color, + lineStyle: LineStyle.Dotted, + lineVisible: true, + lineWidth: 1, + price: price, + title: title, + } + } + + function buildMarker( + shape: SeriesMarkerShape, + color: string, + direction: TradeDirection, + date: Date, + text?: string + ): SeriesMarker
    + + ) +} + +export default ScenarioTable diff --git a/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx b/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx new file mode 100644 index 0000000..e75e916 --- /dev/null +++ b/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx @@ -0,0 +1,401 @@ +import React, { useEffect, useState } from 'react' +import type { SubmitHandler } from 'react-hook-form' +import { useForm } from 'react-hook-form' +import 'react-toastify/dist/ReactToastify.css' + +import useApiUrlStore from '../../app/store/apiStore' +import { Toast } from '../../components/mollecules' +import type { Strategy } from '../../generated/ManagingApi' +import { + StrategyType, + ScenarioClient, + Timeframe, +} from '../../generated/ManagingApi' + +import StrategyTable from './strategyTable' + +interface IStrategyFormInput { + type: StrategyType + timeframe: Timeframe + name: string + period: number + fastPeriods: number + slowPeriods: number + signalPeriods: number + multiplier: number + stochPeriods: number + smoothPeriods: number + cyclePeriods: number +} + +const StrategyList: React.FC = () => { + const [strategyType, setStrategyType] = useState( + StrategyType.RsiDivergence + ) + const [strategies, setStrategies] = useState([]) + const [showModal, setShowModal] = useState(false) + const { register, handleSubmit } = useForm() + const { apiUrl } = useApiUrlStore() + const scenarioClient = new ScenarioClient({}, apiUrl) + + async function createStrategy(form: IStrategyFormInput) { + const t = new Toast('Creating strategy') + await scenarioClient + .scenario_CreateStrategy( + form.type, + form.timeframe, + form.name, + form.period, + form.fastPeriods, + form.slowPeriods, + form.signalPeriods, + form.multiplier, + form.stochPeriods, + form.smoothPeriods, + form.cyclePeriods + ) + .then((strategy: Strategy) => { + t.update('success', 'Strategy created') + setStrategies((arr) => [...arr, strategy]) + }) + .catch((err) => { + t.update('error', err) + }) + } + + function setStrategyTypeEvent(e: any) { + setStrategyType(e.target.value) + } + + const onSubmit: SubmitHandler = async (form) => { + closeModal() + await createStrategy(form) + } + + useEffect(() => { + scenarioClient.scenario_GetStrategies().then((data) => { + setStrategies(data) + }) + }, []) + + function openModal() { + setShowModal(true) + } + + function closeModal() { + setShowModal(false) + } + + return ( +
    +
    + + + {showModal ? ( + <> +
    +
    +
    +
    + +
    + Strategy builder +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + {strategyType == StrategyType.EmaTrend || + strategyType == StrategyType.RsiDivergence || + strategyType == StrategyType.StDev || + strategyType == StrategyType.EmaCross || + strategyType == StrategyType.RsiDivergenceConfirm ? ( + <> +
    +
    + + +
    +
    + + ) : null} + + {strategyType == StrategyType.MacdCross ? ( + <> +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + ) : null} + + {strategyType == StrategyType.Stc ? ( + <> +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + ) : null} + + {strategyType == StrategyType.SuperTrend || + strategyType == StrategyType.ChandelierExit ? ( + <> +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + ) : null} + + {strategyType == StrategyType.StochRsiTrend ? ( + <> +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + ) : null} +
    + +
    +
    +
    + +
    + + ) : null} +
    +
    + ) +} + +export default StrategyList diff --git a/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx b/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx new file mode 100644 index 0000000..6743d86 --- /dev/null +++ b/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx @@ -0,0 +1,94 @@ +import { TrashIcon } from '@heroicons/react/solid' +import React, { useEffect, useState } from 'react' + +import useApiUrlStore from '../../app/store/apiStore' +import { SelectColumnFilter, Table, Toast } from '../../components/mollecules' +import type { Strategy } from '../../generated/ManagingApi' +import { ScenarioClient } from '../../generated/ManagingApi' + +interface IStrategyList { + list: Strategy[] +} + +const StrategyTable: React.FC = ({ list }) => { + const [rows, setRows] = useState([]) + const { apiUrl } = useApiUrlStore() + + async function deleteBacktest(id: string) { + const t = new Toast('Deleting strategy') + const client = new ScenarioClient({}, apiUrl) + + await client + .scenario_DeleteStrategy(id) + .then(() => { + t.update('info', 'Strategy deleted') + }) + .catch((err) => { + t.update('error', err) + }) + } + + const columns = React.useMemo( + () => [ + { + Header: 'Name', + accessor: 'name', + disableFilters: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Type', + accessor: 'type', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Timeframe', + accessor: 'timeframe', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Signal', + accessor: 'signalType', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Period', + accessor: 'period', + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <> +
    + +
    + + ), + Header: '', + accessor: 'id', + disableFilters: true, + }, + ], + [] + ) + + useEffect(() => { + setRows(list) + }, [list]) + + return ( +
    +
    + + ) +} + +export default StrategyTable diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx new file mode 100644 index 0000000..84681f0 --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx @@ -0,0 +1,178 @@ +import { useState } from 'react' +import type { SubmitHandler } from 'react-hook-form' +import { useForm } from 'react-hook-form' + +import useApiUrlStore from '../../../app/store/apiStore' +import { Modal, Toast } from '../../../components/mollecules' +import type { Account } from '../../../generated/ManagingApi' +import { + AccountType, + AccountClient, + TradingExchanges, +} from '../../../generated/ManagingApi' +import type { IAccountFormInput, IModalProps } from '../../../global/type' + +const AccountModal: React.FC = ({ showModal, toggleModal }) => { + const [selectedExchange, setSelectedExchange] = useState() + const [selectedType, setSelectedType] = useState() + const { register, handleSubmit } = useForm() + const { apiUrl } = useApiUrlStore() + + async function createMoneyManagement(form: IAccountFormInput) { + const t = new Toast('Creating account') + const client = new AccountClient({}, apiUrl) + const a: Account = { + exchange: form.exchange, + key: form.key, + name: form.name, + secret: form.secret, + type: form.type, + } + await client + .account_PostAccount(a) + .then(() => { + t.update('success', 'Account created') + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + const onSubmit: SubmitHandler = async (form) => { + // @ts-ignore + toggleModal() + await createMoneyManagement(form) + } + + function setSelectedExchangeEvent(e: any) { + setSelectedExchange(e.target.value) + } + + function setSelectedTypeEvent(e: any) { + setSelectedType(e.target.value) + } + + return ( +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + + {selectedExchange != TradingExchanges.Evm && + selectedType != AccountType.Trader ? ( + <> +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + + ) : null} + + {selectedExchange == TradingExchanges.Evm && + selectedType == AccountType.Watch ? ( + <> +
    +
    + + +
    +
    + + ) : null} + +
    + +
    +
    +
    + ) +} + +export default AccountModal diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountRowDetails.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountRowDetails.tsx new file mode 100644 index 0000000..47d8415 --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/account/accountRowDetails.tsx @@ -0,0 +1,63 @@ +import React from 'react' + +import { SelectColumnFilter, Table } from '../../../components/mollecules' +import { IAccountRowDetail } from '../../../global/type' + +const columns = [ + { + Header: 'Chain', + accessor: 'chain.name', + disableFilters: true, + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Assets', + accessor: 'tokenName', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <> +
    + {cell.row.values.amount.toFixed(4)} +
    + + ), + Header: 'Quantity', + accessor: 'amount', + disableFilters: true, + }, + { + Cell: ({ cell }: any) => <>{cell.row.values.value.toFixed(2)} $, + Header: 'USD', + accessor: 'value', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => <> {cell.row.values.price} $, + Header: 'Price', + accessor: 'price', + disableFilters: true, + }, +] + +const AccountRowDetails: React.FC = ({ + balances, + showTotal, +}) => { + return ( + <> +
    + + ) +} + +export default AccountRowDetails diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountSettings.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountSettings.tsx new file mode 100644 index 0000000..708dde6 --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/account/accountSettings.tsx @@ -0,0 +1,47 @@ +import React, { useEffect, useState } from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import type { Account } from '../../../generated/ManagingApi' +import { AccountClient } from '../../../generated/ManagingApi' + +import AccountModal from './accountModal' +import AccountTable from './accountTable' + +const AccountSettings: React.FC = () => { + const [accounts, setAccounts] = useState([]) + const [showModal, setShowModal] = useState(false) + const { apiUrl } = useApiUrlStore() + const [isFetching, setIsFetching] = useState(false) + + useEffect(() => { + const client = new AccountClient({}, apiUrl) + setIsFetching(true) + client + .account_GetAccountsBalances() + .then((data) => { + setAccounts(data) + }) + .finally(() => setIsFetching(false)) + }, []) + + function toggleModal() { + setShowModal(!showModal) + } + + function openModal() { + setShowModal(true) + } + + return ( +
    +
    + + + +
    +
    + ) +} +export default AccountSettings diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountTable.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountTable.tsx new file mode 100644 index 0000000..5574e3c --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/account/accountTable.tsx @@ -0,0 +1,163 @@ +import { + ChevronDownIcon, + ChevronRightIcon, + ClipboardCopyIcon, + TrashIcon, +} from '@heroicons/react/solid' +import React, { useEffect, useState, useMemo } from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import { + SelectColumnFilter, + Table, + Toast, +} from '../../../components/mollecules' +import type { Account } from '../../../generated/ManagingApi' +import { AccountClient } from '../../../generated/ManagingApi' + +import AccountRowDetails from './accountRowDetails' + +interface IAccountList { + list: Account[] + isFetching: boolean +} + +const AccountTable: React.FC = ({ list, isFetching }) => { + const [rows, setRows] = useState([]) + const { apiUrl } = useApiUrlStore() + + async function deleteAcount(accountName: string) { + const t = new Toast('Deleting money management') + const client = new AccountClient({}, apiUrl) + + await client + .account_DeleteAccount(accountName) + .then(() => { + t.update('success', 'Account deleted') + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + + const columns = useMemo( + () => [ + { + Cell: ({ row }: any) => ( + + {row.isExpanded ? ( + + ) : ( + + )} + + ), + + // Make sure it has an ID + Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }: any) => ( + + {isAllRowsExpanded ? 'v' : '>'} + + ), + // Build our expander column + id: 'expander', + }, + { + Header: 'Name', + accessor: 'name', + disableFilters: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Exchange', + accessor: 'exchange', + disableSortBy: true, + }, + { + Filter: SelectColumnFilter, + Header: 'Type', + accessor: 'type', + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <> +
    + {cell.row.values.key.substring(0, 6)}... + {cell.row.values.key.slice(-4)} +
    + + ), + Header: 'Key', + accessor: 'key', + disableFilters: true, + }, + { + Cell: ({ cell }: any) => ( + <> +
    + +
    +
    + +
    + + ), + Header: 'Actions', + accessor: 'id', + disableFilters: true, + }, + ], + [] + ) + + useEffect(() => { + setRows(list) + }, [list]) + + const renderRowSubComponent = React.useCallback( + ({ row }: any) => ( + <> + {row.original.balances != undefined ? ( + + ) : ( +
    No balances
    + )} + + ), + [] + ) + + return ( + <> + {isFetching ? ( +
    + +
    + ) : ( +
    + )} + + ) +} + +export default AccountTable diff --git a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagement.tsx b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagement.tsx new file mode 100644 index 0000000..7e5bbda --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagement.tsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import type { MoneyManagement } from '../../../generated/ManagingApi' +import { MoneyManagementClient } from '../../../generated/ManagingApi' + +import MoneyManagementModal from './moneyManagementModal' +import MoneyManagementTable from './moneymanagementTable' + +const MoneyManagementSettings: React.FC = () => { + const [moneyManagements, setMoneyManagements] = useState( + [] + ) + const [showModal, setShowModal] = useState(false) + const { apiUrl } = useApiUrlStore() + + useEffect(() => { + const client = new MoneyManagementClient({}, apiUrl) + client.moneyManagement_GetMoneyManagements().then((data) => { + setMoneyManagements(data) + }) + }, []) + + function toggleModal() { + setShowModal(!showModal) + } + + function openModal() { + setShowModal(true) + } + + function closeModal() { + setShowModal(false) + } + + return ( +
    +
    + + + +
    +
    + ) +} +export default MoneyManagementSettings diff --git a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagementModal.tsx b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagementModal.tsx new file mode 100644 index 0000000..79043dd --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagementModal.tsx @@ -0,0 +1,190 @@ +import { useEffect, useState } from 'react' +import type { SubmitHandler } from 'react-hook-form' +import { useForm } from 'react-hook-form' + +import useApiUrlStore from '../../../app/store/apiStore' +import { Slider } from '../../../components/atoms' +import { FormInput, Modal, Toast } from '../../../components/mollecules' +import type { MoneyManagement } from '../../../generated/ManagingApi' +import { + MoneyManagementClient, + Timeframe, +} from '../../../generated/ManagingApi' +import type { + IMoneyManagementModalProps, + IMoneyManagementFormInput, +} from '../../../global/type' + +const MoneyManagementModal: React.FC = ({ + showModal, + onClose, + moneyManagement, + disableInputs = false, +}) => { + const [balanceAtRisk, setBalanceAtRisk] = useState(5) + const [takeProfit, setTakeProfit] = useState(20) + const [name, setName] = useState('') + const [stopLoss, setStopLoss] = useState(10) + const [leverage, setLeverage] = useState(1) + const [timeframe, setTimeframe] = useState( + Timeframe.FifteenMinutes + ) + const { reset, register, handleSubmit } = useForm() + const { apiUrl } = useApiUrlStore() + + async function createMoneyManagement(form: IMoneyManagementFormInput) { + const t = new Toast('Creating settings') + const client = new MoneyManagementClient({}, apiUrl) + const mm: MoneyManagement = { + balanceAtRisk: balanceAtRisk / 100, + leverage: leverage, + name: name, + stopLoss: stopLoss / 100, + takeProfit: takeProfit / 100, + timeframe: form.timeframe, + } + + await client + .moneyManagement_PostMoneyManagement(mm) + .then(() => { + t.update('success', 'Settings created') + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + const onSubmit: SubmitHandler = async (form) => { + // @ts-ignore + await createMoneyManagement(form) + onClose() + } + + function onChangeName(e: any) { + setName(e.target.value) + } + + function onTakeProfitChange(e: any) { + setTakeProfit(e.target.value) + } + + function onStopLossChange(e: any) { + setStopLoss(e.target.value) + } + + function onLeverageChange(e: any) { + setLeverage(e.target.value) + } + + useEffect(() => { + if (moneyManagement) { + setBalanceAtRisk(moneyManagement.balanceAtRisk * 100) + setTakeProfit(moneyManagement.takeProfit * 100) + setStopLoss(moneyManagement.stopLoss * 100) + setLeverage(moneyManagement.leverage) + setTimeframe(moneyManagement.timeframe) + setName(moneyManagement.name) + + const defaultValues: MoneyManagement = { + balanceAtRisk: moneyManagement.balanceAtRisk, + leverage: moneyManagement.leverage, + name: moneyManagement.name || '', + stopLoss: moneyManagement.stopLoss, + takeProfit: moneyManagement.takeProfit, + timeframe: moneyManagement.timeframe, + } + reset({ ...defaultValues }) + } + }, [showModal, moneyManagement]) + + return ( + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    {(takeProfit / stopLoss).toFixed(2)}
    +
    +
    + {disableInputs ? null : ( +
    + +
    + )} +
    + ) +} + +export default MoneyManagementModal diff --git a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneymanagementTable.tsx b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneymanagementTable.tsx new file mode 100644 index 0000000..7cfea9e --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneymanagementTable.tsx @@ -0,0 +1,133 @@ +import { PencilAltIcon, TrashIcon } from '@heroicons/react/solid' +import React, { useEffect, useState } from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import { + Toast, + SelectColumnFilter, + Table, +} from '../../../components/mollecules' +import type { MoneyManagement } from '../../../generated/ManagingApi' +import { MoneyManagementClient } from '../../../generated/ManagingApi' +import type { IMoneyManagementList } from '../../../global/type' + +import MoneyManagementModal from './moneyManagementModal' + +const MoneyManagementTable: React.FC = ({ list }) => { + const [rows, setRows] = useState([]) + const [showModal, setShowModal] = useState(false) + const [selectedRow, setSelectedRow] = useState() + const { apiUrl } = useApiUrlStore() + + async function deleteMoneyManagement(name: string) { + const t = new Toast('Deleting money management') + const client = new MoneyManagementClient({}, apiUrl) + + await client + .moneyManagement_DeleteMoneyManagement(name) + .then(() => { + t.update('success', 'Configuration deleted') + }) + .catch((err) => { + t.update('error', 'Error :' + err) + }) + } + + function toggleModal() { + setShowModal(!showModal) + } + + function openModal(mm: MoneyManagement) { + setSelectedRow(mm) + setShowModal(true) + } + + const columns = React.useMemo( + () => [ + { + Filter: SelectColumnFilter, + Header: 'Timeframe', + accessor: 'timeframe', + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => <>x{cell.row.values.leverage}, + Header: 'Leverage', + accessor: 'leverage', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => <>{cell.row.values.balanceAtRisk * 100} %, + Header: 'Balance used', + accessor: 'balanceAtRisk', + disableFilters: true, + }, + { + Cell: ({ cell }) => <>{cell.row.values.stopLoss * 100} %, + Header: 'SL', + accessor: 'stopLoss', + disableFilters: true, + }, + { + Cell: ({ cell }) => <>{cell.row.values.takeProfit * 100} %, + Header: 'TP', + accessor: 'takeProfit', + disableFilters: true, + }, + { + Cell: ({ cell }) => ( + <> + {(cell.row.values.takeProfit / cell.row.values.stopLoss).toFixed(2)} + + ), + Header: 'R/R', + accessor: 'riskReward', + disableFilters: true, + }, + { + Cell: ({ cell }) => ( + <> +
    + +
    +
    + +
    + + ), + Header: 'Actions', + accessor: 'id', + disableFilters: true, + }, + ], + [] + ) + + useEffect(() => { + setRows(list) + }, [list]) + + return ( +
    +
    + + + ) +} + +export default MoneyManagementTable diff --git a/src/Managing.WebApp/src/pages/settingsPage/settings.tsx b/src/Managing.WebApp/src/pages/settingsPage/settings.tsx new file mode 100644 index 0000000..404d069 --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/settings.tsx @@ -0,0 +1,46 @@ +import { useState } from 'react' + +import { Tabs } from '../../components/mollecules' + +import AccountSettings from './account/accountSettings' +import MoneyManagementSettings from './moneymanagement/moneyManagement' +import Theme from './theme' + +type TabsType = { + label: string + index: number + Component: React.FC<{}> +}[] + +// Tabs Array +const tabs: TabsType = [ + { + Component: MoneyManagementSettings, + index: 1, + label: 'Money Management', + }, + { + Component: AccountSettings, + index: 2, + label: 'Account Settings', + }, + { + Component: Theme, + index: 3, + label: 'Theme', + }, +] + +const Settings: React.FC = () => { + const [selectedTab, setSelectedTab] = useState(tabs[0].index) + + return ( +
    +
    + +
    +
    + ) +} + +export default Settings diff --git a/src/Managing.WebApp/src/pages/settingsPage/theme.tsx b/src/Managing.WebApp/src/pages/settingsPage/theme.tsx new file mode 100644 index 0000000..a4b4515 --- /dev/null +++ b/src/Managing.WebApp/src/pages/settingsPage/theme.tsx @@ -0,0 +1,14 @@ +import { ThemeSelector } from '../../components/mollecules' + +const Theme: React.FC = () => { + return ( +
    +

    Settings

    +

    +

    Theme

    + +
    + ) +} + +export default Theme diff --git a/src/Managing.WebApp/src/pages/toolsPage/rektFees.tsx b/src/Managing.WebApp/src/pages/toolsPage/rektFees.tsx new file mode 100644 index 0000000..24f8f3c --- /dev/null +++ b/src/Managing.WebApp/src/pages/toolsPage/rektFees.tsx @@ -0,0 +1,158 @@ +import { useEffect, useState } from 'react' +import type { SubmitHandler } from 'react-hook-form' +import { useForm } from 'react-hook-form' +import Plot from 'react-plotly.js' + +import useTheme from '../../hooks/useTheme' + +interface IRektToolInput { + fundingFeeRate: number + feePerTx: number + averageTradeTime: number +} + +const RektFees = () => { + const { register, handleSubmit } = useForm() + const [amountPerPosition, setAmountPerPosition] = useState([]) + const [z, setZ] = useState([]) + const [fundingFeesRate, setFundingFeesRate] = useState(0.0051) + const [feePerTx, setFeePerTx] = useState(0.13) + const [averageTradeTime, setAverageTradeTime] = useState(960) + const { themeProperty } = useTheme() + const theme = themeProperty() + const onSubmit: SubmitHandler = async (form) => { + setFundingFeesRate(form.fundingFeeRate) + setFeePerTx(form.feePerTx) + setAverageTradeTime(form.averageTradeTime) + } + + useEffect(() => { + updateAmountPerPosition() + updateAxis() + }, [fundingFeesRate, feePerTx, averageTradeTime]) + + function updateAmountPerPosition() { + const array: number[] = [] + for (let i = 300; i < 3500; i += 10) { + array.push(i) + } + setAmountPerPosition(array) + } + + function updateAxis() { + const tempZ: number[][] = [] + for (let i = 0; i < amountPerPosition.length; i++) { + const feePerPosition = getFeesForPosition(amountPerPosition[i]) + const zAxis: number[] = [] + + for (let t = 1; t < 1000; t++) { + const totalFees = feePerPosition * t + zAxis.push((totalFees * 100) / amountPerPosition[i]) + } + + tempZ.push(zAxis) + } + setZ(tempZ) + } + + function getFeesForPosition(amountPerPosition: number): number { + const tradeTime = averageTradeTime / 60 + const fundingFees = amountPerPosition * tradeTime * (fundingFeesRate / 100) + const positionFees = amountPerPosition * 0.0001 + const txFees = feePerTx * 2 // Open + SL or TP + + return fundingFees + txFees + positionFees + } + + return ( +
    +
    + + + + + + + +
    + ) +} +export default RektFees diff --git a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlight.tsx b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlight.tsx new file mode 100644 index 0000000..027928a --- /dev/null +++ b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlight.tsx @@ -0,0 +1,76 @@ +import moment from 'moment' +import { useEffect, useState } from 'react' + +import useApiUrlStore from '../../../app/store/apiStore' +import type { + Spotlight, + SpotlightOverview, +} from '../../../generated/ManagingApi' +import { DataClient } from '../../../generated/ManagingApi' + +import SpotlightSummary from './spotlightSummary' +import SpotlightTable from './spotlightTable' + +interface ISpotlightOverview { + overview: SpotlightOverview | undefined +} +const Overview: React.FC = ({ overview }) => { + return ( + <> +
    Last update : {moment(overview?.dateTime).fromNow()}
    +
    + +
    +
    + {overview?.spotlights.map((s) => ( + + ))} +
    + + ) +} + +interface ISpotlight { + spotlight: Spotlight +} +const SpotlightDetail: React.FC = ({ spotlight }) => { + return ( + <> +
    + +
    + + ) +} + +const SpotlightView = () => { + const [overview, setOverview] = useState() + const [isFetching, setIsFetching] = useState(true) + + const { apiUrl } = useApiUrlStore() + const dataClient = new DataClient({}, apiUrl) + + useEffect(() => { + dataClient + .data_GetSpotlight() + .then((data) => { + setOverview(data) + }) + .finally(() => { + setIsFetching(false) + }) + }, []) + + return ( + <> +
    + {isFetching ? ( + + ) : ( + + )} +
    + + ) +} +export default SpotlightView diff --git a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightSummary.tsx b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightSummary.tsx new file mode 100644 index 0000000..68557e9 --- /dev/null +++ b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightSummary.tsx @@ -0,0 +1,210 @@ +import React from 'react' + +import { PieChart } from '../../../components/mollecules' +import type { Signal, Spotlight } from '../../../generated/ManagingApi' +import { TradeDirection } from '../../../generated/ManagingApi' +import useTheme from '../../../hooks/useTheme' + +interface ISpotlightSummary { + spotlights: Spotlight[] | undefined +} + +interface ISummaryChart { + fiveMinutes: ISummaryChartItem + fifteenMinutes: ISummaryChartItem + oneHour: ISummaryChartItem + fourHours: ISummaryChartItem + oneDay: ISummaryChartItem +} + +interface ISummaryChartItem { + long: number + short: number + none: number +} + +const SpotlightSummary: React.FC = ({ spotlights }) => { + const { themeProperty } = useTheme() + const theme = themeProperty() + const labels = ['Long', 'Short', 'None'] + const colors = [theme.success, theme.error, 'grey'] + + function GetSummaryChartItem(signal: Signal) { + const summary: ISummaryChartItem = { + long: 0, + none: 0, + short: 0, + } + if (signal) { + switch (signal.direction) { + case TradeDirection.Long: + summary.long++ + break + case TradeDirection.Short: + summary.short++ + break + case TradeDirection.None: + summary.none++ + break + default: + summary.none++ + break + } + } else { + summary.none++ + } + return summary + } + + function GetSignalChart(): React.ReactNode { + const summary: ISummaryChart = { + fifteenMinutes: { + long: 0, + none: 0, + short: 0, + }, + fiveMinutes: { + long: 0, + none: 0, + short: 0, + }, + fourHours: { + long: 0, + none: 0, + short: 0, + }, + oneDay: { + long: 0, + none: 0, + short: 0, + }, + oneHour: { + long: 0, + none: 0, + short: 0, + }, + } + + if (spotlights) { + spotlights?.forEach((s) => { + s.tickerSignals.forEach((t) => { + const fiveMinutesSummary = GetSummaryChartItem( + t.fiveMinutes[t.fiveMinutes.length - 1] + ) + summary.fiveMinutes.long += fiveMinutesSummary.long + summary.fiveMinutes.short += fiveMinutesSummary.short + summary.fiveMinutes.none += fiveMinutesSummary.none + const fifteenMinutesSummary = GetSummaryChartItem( + t.fifteenMinutes[t.fifteenMinutes.length - 1] + ) + summary.fifteenMinutes.long += fifteenMinutesSummary.long + summary.fifteenMinutes.short += fifteenMinutesSummary.short + summary.fifteenMinutes.none += fifteenMinutesSummary.none + + const oneHour = GetSummaryChartItem(t.oneHour[t.oneHour.length - 1]) + summary.oneHour.long += oneHour.long + summary.oneHour.short += oneHour.short + summary.oneHour.none += oneHour.none + + const fourHours = GetSummaryChartItem( + t.fourHour[t.fourHour.length - 1] + ) + summary.fourHours.long += fourHours.long + summary.fourHours.short += fourHours.short + summary.fourHours.none += fourHours.none + + const oneDay = GetSummaryChartItem(t.oneDay[t.oneDay.length - 1]) + summary.oneDay.long += oneDay.long + summary.oneDay.short += oneDay.short + summary.oneDay.none += oneDay.none + }) + }) + } + + return ( + <> +
    +
    +
    5min
    +
    + +
    +
    +
    +
    15min
    +
    + +
    +
    +
    +
    1h
    +
    + +
    +
    +
    +
    4h
    +
    + +
    +
    +
    +
    1d
    +
    + +
    +
    +
    + + ) + } + + return ( +
    +
    + {GetSignalChart()} +
    + ) +} + +export default SpotlightSummary diff --git a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightTable.tsx b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightTable.tsx new file mode 100644 index 0000000..cfd3f5c --- /dev/null +++ b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightTable.tsx @@ -0,0 +1,94 @@ +import React from 'react' + +import { Table } from '../../../components/mollecules' +import { SpotLightBadge } from '../../../components/organism' +import type { Signal, Spotlight } from '../../../generated/ManagingApi' +import { TradeDirection } from '../../../generated/ManagingApi' + +interface ISpotlightTable { + spotlight: Spotlight +} +const GetBadgeForTimeframe = (signals: Signal[]) => { + const lastSignal = signals[signals.length > 1 ? signals.length - 1 : 0] + return lastSignal ? ( + + ) : ( + + ) +} + +const SpotlightTable: React.FC = ({ spotlight }) => { + const columns = React.useMemo( + () => [ + { + Header: spotlight.scenario.name, + columns: [ + { + Header: 'Ticker', + accessor: 'ticker', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <>{GetBadgeForTimeframe(cell.row.values.fiveMinutes)} + ), + Header: '5m', + accessor: 'fiveMinutes', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <>{GetBadgeForTimeframe(cell.row.values.fifteenMinutes)} + ), + Header: '15m', + accessor: 'fifteenMinutes', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <>{GetBadgeForTimeframe(cell.row.values.oneHour)} + ), + Header: '1h', + accessor: 'oneHour', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <>{GetBadgeForTimeframe(cell.row.values.fourHour)} + ), + Header: '4h', + accessor: 'fourHour', + disableFilters: true, + disableSortBy: true, + }, + { + Cell: ({ cell }: any) => ( + <>{GetBadgeForTimeframe(cell.row.values.oneDay)} + ), + Header: '1D', + accessor: 'oneDay', + disableFilters: true, + disableSortBy: true, + }, + ], + }, + ], + [] + ) + + return ( +
    +
    + + ) +} + +export default SpotlightTable diff --git a/src/Managing.WebApp/src/pages/toolsPage/tools.tsx b/src/Managing.WebApp/src/pages/toolsPage/tools.tsx new file mode 100644 index 0000000..94c9839 --- /dev/null +++ b/src/Managing.WebApp/src/pages/toolsPage/tools.tsx @@ -0,0 +1,36 @@ +import { useState, useEffect } from 'react' + +import { Tabs } from '../../components/mollecules' +import type { ITabsType } from '../../global/type' + +import RektFees from './rektFees' +import SpotlightView from './spotlight/spotlight' + +const tabs: ITabsType = [ + { + Component: SpotlightView, + index: 1, + label: 'Spotlight', + }, + { + Component: RektFees, + index: 2, + label: 'RektFees', + }, +] + +const Tools: React.FC = () => { + const [selectedTab, setSelectedTab] = useState(tabs[0].index) + + useEffect(() => {}, []) + + return ( +
    +
    + +
    +
    + ) +} + +export default Tools diff --git a/src/Managing.WebApp/src/pages/web3Page/web3.tsx b/src/Managing.WebApp/src/pages/web3Page/web3.tsx new file mode 100644 index 0000000..fb60113 --- /dev/null +++ b/src/Managing.WebApp/src/pages/web3Page/web3.tsx @@ -0,0 +1,104 @@ +import type { Contract } from 'ethers' +import { ethers } from 'ethers' +import { useRef, useState } from 'react' + +const Web3 = () => { + const moodInputRef = useRef() + const [mood, setMood] = useState() + //@ts-ignore + const provider = new ethers.providers.Web3Provider(window.ethereum, 'ropsten') + const contractAddress = '0x0335e801159Af04b3067bED0aeeaCC86Ece51e19' + const moodAbi = [ + { + constant: true, + inputs: [], + name: 'getMood', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + signature: '0x9d0c1397', + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'string', + name: '_mood', + type: 'string', + }, + ], + name: 'setMood', + outputs: [], + signature: '0x5f3cbff5', + stateMutability: 'nonpayable', + type: 'function', + }, + ] + + let contract: Contract + let signer + + provider.send('eth_requestAccounts', []).then(() => { + provider.listAccounts().then(function (accounts) { + signer = provider.getSigner(accounts[0]) + contract = new ethers.Contract(contractAddress, moodAbi, signer) + }) + }) + + async function getMoodAbi() { + const getMoodPromise = contract.getMood() + const currentMood = await getMoodPromise + setMood(currentMood) + } + + async function setMoodAbi() { + //@ts-ignore + const setMoodPromise = contract.setMood(moodInputRef.current.value) + await setMoodPromise + } + + return ( +
    +
    +

    Web3 Playground

    +
    +
    +

    dApp

    + + + + +
    +
    +

    {mood}

    +
    + +
    +
    +
    +
    +
    +

    NFT

    +
    +
    +
    +
    + ) +} + +export default Web3 diff --git a/src/Managing.WebApp/src/pages/workflow/workflows.tsx b/src/Managing.WebApp/src/pages/workflow/workflows.tsx new file mode 100644 index 0000000..205b728 --- /dev/null +++ b/src/Managing.WebApp/src/pages/workflow/workflows.tsx @@ -0,0 +1,98 @@ +import { useQuery } from '@tanstack/react-query' +import { useEffect, useState } from 'react' +import type { Edge, Node } from 'reactflow' + +import useApiUrlStore from '../../app/store/apiStore' +import { Loader } from '../../components/atoms' +import { Tabs } from '../../components/mollecules' +import { WorkflowCanvas } from '../../components/organism' +import type { + IFlow, + SyntheticFlow, + SyntheticWorkflow, +} from '../../generated/ManagingApi' +import { WorkflowClient } from '../../generated/ManagingApi' +import type { ITabsType, IWorkflow } from '../../global/type' + +const mapWorkflowToTabs = (workflows: SyntheticWorkflow[]): ITabsType => { + return workflows.map((workflow: SyntheticWorkflow, index: number) => { + return { + Component: WorkflowCanvas, + index: index, + label: workflow.name, + props: mapFlowsToNodes(workflow.flows, workflow.name), + } + }) +} + +const mapFlowsToNodes = (flows: SyntheticFlow[], name: string): IWorkflow => { + const nodes: Node[] = [] + const edges: Edge[] = [] + + flows.forEach((flow: SyntheticFlow) => { + nodes.push(mapFlowToNode(flow)) + }) + + for (const node of nodes) { + const childrenNodes = nodes.filter((n) => n.data.parentId == node.data.id) + + if (childrenNodes.length > 0) { + childrenNodes.forEach((childNode) => { + edges.push({ + id: `${node.id}-${childNode.id}`, + source: node.id, + target: childNode.id, + }) + }) + } + } + + return { edges, name: name, nodes } as IWorkflow +} + +const mapFlowToNode = (flow: SyntheticFlow): Node => { + return { + data: flow, + id: flow.id, + position: { x: 0, y: 0 }, + type: flow.type, + } +} + +const Workflows: React.FC = () => { + const [selectedTab, setSelectedTab] = useState(1) + const { apiUrl } = useApiUrlStore() + const client = new WorkflowClient({}, apiUrl) + + const { data, isLoading } = useQuery({ + onSuccess: () => { + setSelectedTab(0) + }, + queryFn: () => client.workflow_GetWorkflows(), + queryKey: ['workflows'], + }) + + useEffect(() => {}, [isLoading]) + + if (isLoading || data == null) { + return + } + + return ( +
    +
    + { + console.log('add button clicked') + }} + /> +
    +
    + ) +} + +export default Workflows diff --git a/src/Managing.WebApp/src/polyfills.ts b/src/Managing.WebApp/src/polyfills.ts new file mode 100644 index 0000000..dc3665c --- /dev/null +++ b/src/Managing.WebApp/src/polyfills.ts @@ -0,0 +1,4 @@ +window.global = window.global ?? window +window.process = window.process ?? { env: {} } + +export {} diff --git a/src/Managing.WebApp/src/smartcontracts/courses/mood.sol b/src/Managing.WebApp/src/smartcontracts/courses/mood.sol new file mode 100644 index 0000000..68f90b7 --- /dev/null +++ b/src/Managing.WebApp/src/smartcontracts/courses/mood.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.1; + +contract MoodDiary{ + string mood; + + //create a function that writes a mood to the smart contract + function setMood(string memory _mood) public{ + mood = _mood; + } + + //create a function the reads the mood from the smart contract + function getMood() public view returns(string memory){ + return mood; + } + } \ No newline at end of file diff --git a/src/Managing.WebApp/src/smartcontracts/courses/odaNFT.sol b/src/Managing.WebApp/src/smartcontracts/courses/odaNFT.sol new file mode 100644 index 0000000..378a154 --- /dev/null +++ b/src/Managing.WebApp/src/smartcontracts/courses/odaNFT.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Import the openzepplin contracts +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +// GameItem is ERC721 signifies that the contract we are creating imports ERC721 and follows ERC721 contract from openzeppelin +contract GameItem is ERC721 { + + constructor() ERC721("GameItem", "ITM") { + // mint an NFT to yourself + _mint(msg.sender, 1); + } +} \ No newline at end of file diff --git a/src/Managing.WebApp/src/smartcontracts/courses/odaToken.sol b/src/Managing.WebApp/src/smartcontracts/courses/odaToken.sol new file mode 100644 index 0000000..64743a8 --- /dev/null +++ b/src/Managing.WebApp/src/smartcontracts/courses/odaToken.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol"; + +contract OdaToken is ERC20 { + constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) { + _mint(msg.sender, 10 * 10 ** 18); + } +} \ No newline at end of file diff --git a/src/Managing.WebApp/src/stores/store.tsx b/src/Managing.WebApp/src/stores/store.tsx new file mode 100644 index 0000000..81c5d23 --- /dev/null +++ b/src/Managing.WebApp/src/stores/store.tsx @@ -0,0 +1,3 @@ +import { atomWithStorage } from 'jotai/utils' + +export const themeAtom = atomWithStorage('theme', 'black') diff --git a/src/Managing.WebApp/src/styles/app.css b/src/Managing.WebApp/src/styles/app.css new file mode 100644 index 0000000..89065d8 --- /dev/null +++ b/src/Managing.WebApp/src/styles/app.css @@ -0,0 +1,46 @@ +.App-logo { + width: 1.5em; +} + +@media (prefers-reduced-motion: no-preference) { + /* .App-logo { + animation: App-logo-spin infinite 20s linear; + } */ +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +button { + font-size: calc(10px + 2vmin); +} + +.notificationWrapper { + @apply w-96 hover:shadow-none transform-gpu hover:translate-y-1 rounded-xl relative flex flex-row items-center justify-between px-4 py-6 text-white transition-all duration-500 ease-in-out translate-y-0 bg-gray-900 shadow-2xl; +} + +.iconWrapper { + @apply text-xl; +} + +.contentWrapper { + @apply flex flex-col items-start justify-center ml-4 cursor-default; +} + +.contentWrapper h1 { + @apply text-base font-semibold leading-none tracking-wider text-gray-200; +} + +.contentWrapper p { + @apply mt-2 text-sm leading-relaxed tracking-wider text-gray-400; +} + +.closeIcon { + @apply top-2 right-2 absolute text-lg cursor-pointer; +} diff --git a/src/Managing.WebApp/src/styles/globals.css b/src/Managing.WebApp/src/styles/globals.css new file mode 100644 index 0000000..da51981 --- /dev/null +++ b/src/Managing.WebApp/src/styles/globals.css @@ -0,0 +1,23 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + margin: 0; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + + code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; + } +} + +@layer utilities { + .layout { + @apply sm:w-11/12 w-10/12 mx-auto; + } +} diff --git a/src/Managing.WebApp/src/vite-env.d.ts b/src/Managing.WebApp/src/vite-env.d.ts new file mode 100644 index 0000000..96204cc --- /dev/null +++ b/src/Managing.WebApp/src/vite-env.d.ts @@ -0,0 +1,3 @@ +/// + +declare type AnyFunction = (...args: any[]) => any diff --git a/src/Managing.WebApp/tailwind.config.js b/src/Managing.WebApp/tailwind.config.js new file mode 100644 index 0000000..c57aa29 --- /dev/null +++ b/src/Managing.WebApp/tailwind.config.js @@ -0,0 +1,12 @@ +module.exports = { + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], + daisyui: { + themes: true, + }, + plugins: [require('@tailwindcss/typography'), require('daisyui')], + theme: { + container: { + center: true, + }, + }, +} diff --git a/src/Managing.WebApp/tsconfig.json b/src/Managing.WebApp/tsconfig.json new file mode 100644 index 0000000..9f749f4 --- /dev/null +++ b/src/Managing.WebApp/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["./src", "hardhat.config.js"], + "exclude": ["node_modules"] +} diff --git a/src/Managing.WebApp/vite.config.ts b/src/Managing.WebApp/vite.config.ts new file mode 100644 index 0000000..449a7b8 --- /dev/null +++ b/src/Managing.WebApp/vite.config.ts @@ -0,0 +1,20 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + sourcemap: true, + target: 'es2020', + }, + optimizeDeps: { + esbuildOptions: { + target: 'es2020', + }, + }, + plugins: [react()], + publicDir: 'assets', + server: { + host: true, + open: true, + }, +}) diff --git a/src/Managing.sln b/src/Managing.sln new file mode 100644 index 0000000..0a20804 --- /dev/null +++ b/src/Managing.sln @@ -0,0 +1,245 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32126.317 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1-Api", "1-Api", "{A1296069-2816-43D4-882C-516BCB718D03}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2-Application", "2-Application", "{F6774DB0-DF13-4077-BC94-0E67EE105C4C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3-Domain", "3-Domain", "{A12DD713-FC6B-4207-ACE4-3883E7275378}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4-Infrastructure", "4-Infrastructure", "{E453D33B-5C2B-4AA1-834D-2C916EC95FC6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Api", "Managing.Api\Managing.Api.csproj", "{0ABF3894-9AC9-4D6A-AEFB-DF0047904018}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Application", "Managing.Application\Managing.Application.csproj", "{5BAD133F-B47B-4270-9C63-97A1C070B4DD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5-Shared", "5-Shared", "{D6711C71-A263-4398-8DFF-28E2CD1FE0CE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Bootstrap", "Managing.Bootstrap\Managing.Bootstrap.csproj", "{2E1D1E52-703E-403A-B563-A3BDA56469B6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Infrastructure.Exchanges", "Managing.Infrastructure.Exchanges\Managing.Infrastructure.Exchanges.csproj", "{C2ADC412-B001-4ECE-9371-977509FB58FB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Common", "Managing.Common\Managing.Common.csproj", "{B097137F-2222-43D5-8398-486CCB45B0B7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Domain", "Managing.Domain\Managing.Domain.csproj", "{BFB1C04B-F70B-4691-BD06-442D203A6F4C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Core", "Managing.Core\Managing.Core.csproj", "{31EF36F8-C057-4571-B942-EAB1E33331F3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0-Documentation", "0-Documentation", "{CC3542A4-2C56-4BF7-A705-A06C974BECDF}" + ProjectSection(SolutionItems) = preProject + ..\Documentation.md = ..\Documentation.md + ..\README.md = ..\README.md + ..\assets\Todo-v1.txt = ..\assets\Todo-v1.txt + ..\assets\Todo-v2.md = ..\assets\Todo-v2.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "6-Tests", "6-Tests", "{8F2ECEA7-5BCA-45DF-B6E3-88AADD7AFD45}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Infrastructure.Tests", "Managing.Infrastructure.Tests\Managing.Infrastructure.Tests.csproj", "{7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Infrastructure.Storage", "Managing.Infrastructure.Storage\Managing.Infrastructure.Storage.csproj", "{837B12AD-E96C-40CE-9DEE-931442A6C15E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Application.Tests", "Managing.Application.Tests\Managing.Application.Tests.csproj", "{35A05E76-29F6-4DC1-886D-FD69926CB490}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Infrastructure.Messengers", "Managing.Infrastructure.Messengers\Managing.Infrastructure.Messengers.csproj", "{AD40302A-27C7-4E9D-B644-C7B141571EAF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Api.Workers", "Managing.Api.Workers\Managing.Api.Workers.csproj", "{0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Application.Workers", "Managing.Application.Workers\Managing.Application.Workers.csproj", "{F0BE6092-102B-43C5-9CAB-88EA28AADC2D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Infrastructure.Databases", "Managing.Infrastructure.Database\Managing.Infrastructure.Databases.csproj", "{E6CB238E-8F60-4139-BDE6-31534832198E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Application.Abstractions", "Managing.Application.Abstractions\Managing.Application.Abstractions.csproj", "{283AC491-97C3-49E0-AB17-272EFB4E5A9C}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{185F8899-EF44-4D83-99C1-303751E2249C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Infrastructure.Evm", "Managing.Infrastructure.Web3\Managing.Infrastructure.Evm.csproj", "{CDDF92D4-9D2E-4134-BD44-3064D6EF462D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Managing.Tools.ABI", "Managing.Tools.ABI\Managing.Tools.ABI.csproj", "{A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{29FD3672-88EF-406D-A372-32AC121A6A52}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Debug|x64.ActiveCfg = Debug|x64 + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Debug|x64.Build.0 = Debug|x64 + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Release|Any CPU.Build.0 = Release|Any CPU + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Release|x64.ActiveCfg = Release|x64 + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018}.Release|x64.Build.0 = Release|x64 + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Debug|x64.ActiveCfg = Debug|x64 + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Debug|x64.Build.0 = Debug|x64 + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Release|Any CPU.Build.0 = Release|Any CPU + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Release|x64.ActiveCfg = Release|x64 + {5BAD133F-B47B-4270-9C63-97A1C070B4DD}.Release|x64.Build.0 = Release|x64 + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Debug|x64.ActiveCfg = Debug|x64 + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Debug|x64.Build.0 = Debug|x64 + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Release|Any CPU.Build.0 = Release|Any CPU + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Release|x64.ActiveCfg = Release|x64 + {2E1D1E52-703E-403A-B563-A3BDA56469B6}.Release|x64.Build.0 = Release|x64 + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Debug|x64.ActiveCfg = Debug|x64 + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Debug|x64.Build.0 = Debug|x64 + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Release|Any CPU.Build.0 = Release|Any CPU + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Release|x64.ActiveCfg = Release|x64 + {C2ADC412-B001-4ECE-9371-977509FB58FB}.Release|x64.Build.0 = Release|x64 + {B097137F-2222-43D5-8398-486CCB45B0B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B097137F-2222-43D5-8398-486CCB45B0B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B097137F-2222-43D5-8398-486CCB45B0B7}.Debug|x64.ActiveCfg = Debug|x64 + {B097137F-2222-43D5-8398-486CCB45B0B7}.Debug|x64.Build.0 = Debug|x64 + {B097137F-2222-43D5-8398-486CCB45B0B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B097137F-2222-43D5-8398-486CCB45B0B7}.Release|Any CPU.Build.0 = Release|Any CPU + {B097137F-2222-43D5-8398-486CCB45B0B7}.Release|x64.ActiveCfg = Release|x64 + {B097137F-2222-43D5-8398-486CCB45B0B7}.Release|x64.Build.0 = Release|x64 + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Debug|x64.ActiveCfg = Debug|x64 + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Debug|x64.Build.0 = Debug|x64 + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Release|Any CPU.Build.0 = Release|Any CPU + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Release|x64.ActiveCfg = Release|x64 + {BFB1C04B-F70B-4691-BD06-442D203A6F4C}.Release|x64.Build.0 = Release|x64 + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Debug|x64.ActiveCfg = Debug|x64 + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Debug|x64.Build.0 = Debug|x64 + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Release|Any CPU.Build.0 = Release|Any CPU + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Release|x64.ActiveCfg = Release|x64 + {31EF36F8-C057-4571-B942-EAB1E33331F3}.Release|x64.Build.0 = Release|x64 + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Debug|x64.ActiveCfg = Debug|x64 + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Debug|x64.Build.0 = Debug|x64 + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Release|Any CPU.Build.0 = Release|Any CPU + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Release|x64.ActiveCfg = Release|x64 + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE}.Release|x64.Build.0 = Release|x64 + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Debug|x64.ActiveCfg = Debug|x64 + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Debug|x64.Build.0 = Debug|x64 + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Release|Any CPU.Build.0 = Release|Any CPU + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Release|x64.ActiveCfg = Release|x64 + {837B12AD-E96C-40CE-9DEE-931442A6C15E}.Release|x64.Build.0 = Release|x64 + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Debug|x64.ActiveCfg = Debug|x64 + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Debug|x64.Build.0 = Debug|x64 + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Release|Any CPU.Build.0 = Release|Any CPU + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Release|x64.ActiveCfg = Release|x64 + {35A05E76-29F6-4DC1-886D-FD69926CB490}.Release|x64.Build.0 = Release|x64 + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Debug|x64.ActiveCfg = Debug|Any CPU + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Debug|x64.Build.0 = Debug|Any CPU + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Release|Any CPU.Build.0 = Release|Any CPU + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Release|x64.ActiveCfg = Release|Any CPU + {AD40302A-27C7-4E9D-B644-C7B141571EAF}.Release|x64.Build.0 = Release|Any CPU + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Debug|x64.ActiveCfg = Debug|x64 + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Debug|x64.Build.0 = Debug|x64 + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Release|Any CPU.Build.0 = Release|Any CPU + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Release|x64.ActiveCfg = Release|x64 + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E}.Release|x64.Build.0 = Release|x64 + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Debug|x64.ActiveCfg = Debug|Any CPU + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Debug|x64.Build.0 = Debug|Any CPU + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Release|Any CPU.Build.0 = Release|Any CPU + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Release|x64.ActiveCfg = Release|Any CPU + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D}.Release|x64.Build.0 = Release|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Debug|x64.ActiveCfg = Debug|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Debug|x64.Build.0 = Debug|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Release|Any CPU.Build.0 = Release|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Release|x64.ActiveCfg = Release|Any CPU + {E6CB238E-8F60-4139-BDE6-31534832198E}.Release|x64.Build.0 = Release|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Debug|x64.ActiveCfg = Debug|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Debug|x64.Build.0 = Debug|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Release|Any CPU.Build.0 = Release|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Release|x64.ActiveCfg = Release|Any CPU + {283AC491-97C3-49E0-AB17-272EFB4E5A9C}.Release|x64.Build.0 = Release|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Debug|x64.ActiveCfg = Debug|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Debug|x64.Build.0 = Debug|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Release|Any CPU.Build.0 = Release|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Release|x64.ActiveCfg = Release|Any CPU + {185F8899-EF44-4D83-99C1-303751E2249C}.Release|x64.Build.0 = Release|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Debug|x64.ActiveCfg = Debug|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Debug|x64.Build.0 = Debug|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Release|Any CPU.Build.0 = Release|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Release|x64.ActiveCfg = Release|Any CPU + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D}.Release|x64.Build.0 = Release|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Debug|x64.Build.0 = Debug|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Release|Any CPU.Build.0 = Release|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Release|x64.ActiveCfg = Release|Any CPU + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1}.Release|x64.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {0ABF3894-9AC9-4D6A-AEFB-DF0047904018} = {A1296069-2816-43D4-882C-516BCB718D03} + {5BAD133F-B47B-4270-9C63-97A1C070B4DD} = {F6774DB0-DF13-4077-BC94-0E67EE105C4C} + {2E1D1E52-703E-403A-B563-A3BDA56469B6} = {D6711C71-A263-4398-8DFF-28E2CD1FE0CE} + {C2ADC412-B001-4ECE-9371-977509FB58FB} = {E453D33B-5C2B-4AA1-834D-2C916EC95FC6} + {B097137F-2222-43D5-8398-486CCB45B0B7} = {D6711C71-A263-4398-8DFF-28E2CD1FE0CE} + {BFB1C04B-F70B-4691-BD06-442D203A6F4C} = {A12DD713-FC6B-4207-ACE4-3883E7275378} + {31EF36F8-C057-4571-B942-EAB1E33331F3} = {D6711C71-A263-4398-8DFF-28E2CD1FE0CE} + {7E7BFF3A-E936-4A6E-AC75-D44677C6DADE} = {8F2ECEA7-5BCA-45DF-B6E3-88AADD7AFD45} + {837B12AD-E96C-40CE-9DEE-931442A6C15E} = {E453D33B-5C2B-4AA1-834D-2C916EC95FC6} + {35A05E76-29F6-4DC1-886D-FD69926CB490} = {8F2ECEA7-5BCA-45DF-B6E3-88AADD7AFD45} + {AD40302A-27C7-4E9D-B644-C7B141571EAF} = {E453D33B-5C2B-4AA1-834D-2C916EC95FC6} + {0DC797C2-007C-496E-B4C9-FDBD29D4EF4E} = {A1296069-2816-43D4-882C-516BCB718D03} + {F0BE6092-102B-43C5-9CAB-88EA28AADC2D} = {F6774DB0-DF13-4077-BC94-0E67EE105C4C} + {E6CB238E-8F60-4139-BDE6-31534832198E} = {E453D33B-5C2B-4AA1-834D-2C916EC95FC6} + {283AC491-97C3-49E0-AB17-272EFB4E5A9C} = {F6774DB0-DF13-4077-BC94-0E67EE105C4C} + {CDDF92D4-9D2E-4134-BD44-3064D6EF462D} = {E453D33B-5C2B-4AA1-834D-2C916EC95FC6} + {A1D88DC3-1CF6-4C03-AEEC-30AA37420CE1} = {D6711C71-A263-4398-8DFF-28E2CD1FE0CE} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD7CA081-CE52-4824-9777-C0562E54F3EA} + EndGlobalSection +EndGlobal diff --git a/src/appsettings.Lowpro.json b/src/appsettings.Lowpro.json new file mode 100644 index 0000000..16ef238 --- /dev/null +++ b/src/appsettings.Lowpro.json @@ -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": "1134966063075971144", + "PublicKey": "55bc79e483ffa203a1da1e49588c86cf1345dd66d676ab52c73dbf09cc1b4800", + "TokenId": "MTEzNDk2NjA2MzA3NTk3MTE0NA.GWHiDf.2s2qq3XCI2bftfnqm_0ndcou5LQwZPdsCZ9nQc", + + "SignalChannelId": 1134858150667898910, + "TradesChannelId": 1134858092530634864, + "TroublesChannelId": 1134858233031446671, + "CopyTradingChannelId": 1134857874896588881, + "RequestsChannelId": 1018589494968078356, + "LeaderboardChannelId": 1133169725237633095, + "NoobiesboardChannelId": 1133504653485690940, + "ButtonExpirationMinutes": 10 + + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/src/appsettings.Oda-Sandbox.json b/src/appsettings.Oda-Sandbox.json new file mode 100644 index 0000000..c8c284b --- /dev/null +++ b/src/appsettings.Oda-Sandbox.json @@ -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": "*" +} \ No newline at end of file diff --git a/src/captain-definition b/src/captain-definition new file mode 100644 index 0000000..0d2156e --- /dev/null +++ b/src/captain-definition @@ -0,0 +1,4 @@ +{ + "schemaVersion": 2, + "dockerfilePath": "Managing.Api/Dockerfile" +} diff --git a/src/docker-compose.dcproj b/src/docker-compose.dcproj new file mode 100644 index 0000000..8d956aa --- /dev/null +++ b/src/docker-compose.dcproj @@ -0,0 +1,25 @@ + + + + 2.1 + Linux + 185f8899-ef44-4d83-99c1-303751e2249c + LaunchBrowser + {Scheme}://localhost:{ServicePort} + managing.api + + + + + docker-compose.yml + + + + + + + + + + + \ No newline at end of file diff --git a/src/docker-compose.override.yml b/src/docker-compose.override.yml new file mode 100644 index 0000000..1d050d9 --- /dev/null +++ b/src/docker-compose.override.yml @@ -0,0 +1,29 @@ +version: '3.4' + +services: + + managing.api: + environment: + - ASPNETCORE_ENVIRONMENT=Oda-docker + - ASPNETCORE_URLS=https://+:443;http://+:80 + - ASPNETCORE_Kestrel__Certificates__Default__Password=!MotdepasseFort11 + - ASPNETCORE_Kestrel__Certificates__Default__Path=/src/managing_cert.pfx + ports: + - "82:80" + - "446:443" + volumes: + - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro + - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro + + managing.api.workers: + environment: + - ASPNETCORE_ENVIRONMENT=Oda-docker + - ASPNETCORE_URLS=https://+:443;http://+:80 + - ASPNETCORE_Kestrel__Certificates__Default__Password=!MotdepasseFort11 + - ASPNETCORE_Kestrel__Certificates__Default__Path=/src/managing_cert.pfx + ports: + - "83:80" + - "447:443" + volumes: + - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro + - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro diff --git a/src/docker-compose.yml b/src/docker-compose.yml new file mode 100644 index 0000000..4366a7d --- /dev/null +++ b/src/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.4' +name: dev + +services: + managing.api: + image: ${DOCKER_REGISTRY-}managingapi + build: + context: . + dockerfile: Managing.Api/Dockerfile + networks: + - managing-network + + managing.api.workers: + image: ${DOCKER_REGISTRY-}managingapiworkers + build: + context: . + dockerfile: Managing.Api.Workers/Dockerfile + networks: + - managing-network + +networks: + managing-network: + external: + name: managing-network \ No newline at end of file diff --git a/src/launchSettings.json b/src/launchSettings.json new file mode 100644 index 0000000..2e111a2 --- /dev/null +++ b/src/launchSettings.json @@ -0,0 +1,28 @@ +{ + "profiles": { + "Docker - Api": { + "commandName": "DockerCompose", + "commandVersion": "1.0", + "serviceActions": { + "managing.api": "StartDebugging", + "managing.api.workers": "DoNotStart" + } + }, + "Docker - Workers": { + "commandName": "DockerCompose", + "commandVersion": "1.0", + "serviceActions": { + "managing.api": "DoNotStart", + "managing.api.workers": "StartDebugging" + } + }, + "Docker - Debug all": { + "commandName": "DockerCompose", + "commandVersion": "1.0", + "serviceActions": { + "managing.api": "StartDebugging", + "managing.api.workers": "StartDebugging" + } + } + } +} \ No newline at end of file