From 160fd02aac0ea1fb69ade73be631a36149dc3db3 Mon Sep 17 00:00:00 2001 From: Oda <102867384+CryptoOda@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:06:15 +0700 Subject: [PATCH] Update position worker (#11) * Update position worker * Update todo --- assets/Todo-v2.md | 225 ++++++++++-------- src/Managing.Api.Workers/Program.cs | 1 + .../Workers/PositionManagerWorker.cs | 74 ++++-- .../Controllers/TradingController.cs | 12 +- src/Managing.Application/Bots/TradingBot.cs | 5 +- .../EvmManager.cs | 2 +- .../Services/Gmx/GmxV2Service.cs | 5 + .../src/pages/desk/deskWidget.tsx | 6 +- .../pages/desk/widgets/OpenPositionWidget.tsx | 2 +- 9 files changed, 199 insertions(+), 133 deletions(-) diff --git a/assets/Todo-v2.md b/assets/Todo-v2.md index fb2f0fd..1ee4b8a 100644 --- a/assets/Todo-v2.md +++ b/assets/Todo-v2.md @@ -87,50 +87,11 @@ - [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 +## 4 - Trading desk [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=11070087) -## 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 (Post-pawned) - -- [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 - -_______________________________________________________________________________________________________________ +- [x] Live data position hub +- [x] Live candles +- [x] Open position from desk # Phase 2 - Web3 @@ -141,29 +102,47 @@ ________________________________________________________________________________ - [x] Close trade - [ ] Update position / TP / SL -## 0 - GMX v2 +## Bots -## 1 - Wallet management +- [x] Add button to display money management use by the bot +- [ ] POST POWNER - On the modarl, When simple bot selected, show only a select for the workflow -- [ ] Create onboarding process for new wallet -- [ ] User can deposit token to and evm account with a QR code or clicking on button to copy the address -- [ ] Add account abstraction to level up security +## Workflow -## 2 - [Track address](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32158941) +- [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 +- [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 -- [ ] Create a snapshot of the account balances with BalancesWorker -- [ ] Display balance in $ evolution per token hold on the account page +## Backtests -## 3 - Settings [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32159287) +- [x] Display the money management used by the backtest -- [ ] Save theme into database -- [ ] Call GET Settings/Theme to retrieve the theme used by the user +## Part 1 : Adaptive trading -## 4 - Trading desk [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=11070087) +- [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 +- [x] Bot backup worker: Every x, get saved bots and check if still running. If not running call api to reboot bot. +- [x] Factorize amount management for GMX Service +- [x] Create worker to fetch the biggest spread between long\short funding rate and send alert when most profitable + delta neutral position is found +- [x] Create a worker to scrap funding fees over time (per ticker, per exchange) +- -- [x] Live data position hub -- [x] Live candles -- [x] Open position from desk +## Scenarios + +- [ ] Create a blocky-like editor to create scenarios https://google.github.io/blockly-samples/ ## 5 - Live liquidation map @@ -172,31 +151,94 @@ ________________________________________________________________________________ - [ ] Live data signal - [ ] Display massive liquidation on TradeChartWidget -## 6 - Metrics [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32159107) +## To review +- [ ] ???? Improve strategy composability by using delegate pattern ???? +- [ ] Post-powned - Load signal from database +- [ ] Create a bot to update signal into database that still in pending +- [ ] Signal return best SL and TP +- [ ] Extract BacktestReport from tests to a Tools projects +- [ ] Remove link betweend Domain object to EVM project +- [ ] GMX Service should handle only Nethereum object, not Domain.Models +- [ ] Compute backtests pagination on backend side +- [ ] Implement from/to tickers array pattern + +_______________________________________________________________________________________________________________ + +# Before Kudai + +## Part 1 : Strategies optimisation + +- [ ] Create strategies types from signal (a divergence) / trend (stoch long or short) / context (high vol/low vol) +- [ ] Strategy can return a suggested trade that will give the best orders to execute +- [ ] Optimize all strategies with more data (funding rate trend, bbwp, etc..) +- [ ] Add entry/exit price to signal for adaptive risk +- [ ] Add bbwp indicator => PR + example : https://github.com/DaveSkender/Stock.Indicators/pull/893/files#diff-df1cd2a99846f7328c9fc3db4d2c164380a0fa943cdcad2ece01b886cac68137 +- [ ] Optimize entry by getting last ask price and place an order limit t match directly with it +- [ ] Create a trend strategy on fundinrate -> long trend if negativ fr / short trend if positiv fr +- [ ] MACD: Open only when candle is not a big move to avoid late entry (only enter when low vol) + +## Part 2 : Backtests automation + +- [x] Save start/end time and timeframe into the result +- [ ] 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 +- [ ] Combine multi-timeframe to detect better signals => Adapt risk on the lowest timeframe based on upper timeframe +- [ ] Remove candles from the backtestResult +- [ ] On GET backtest -> Retrieve the candle from cache or database - [ ] 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) + +## Part 3 - Wallet management + +- [ ] Create onboarding process for new wallet +- [ ] User can deposit token to and evm account with a QR code or clicking on button to copy the address +- [ ] Add account abstraction to level up security with privy +- [ ] Display balance in $ evolution per token hold on the account page +- [ ] Create a snapshot of the account balances with BalancesWorker ## 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 -- [x] 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 -- [ ] Bot backup worker: Every x, get saved bots and check if still running. If not running call api to reboot bot. -- [ ] Create worker to fetch the biggest spread between long\short funding rate and send alert when most profitable - delta neutral position is found + +## Backend : + +- [ ] Filter all objects by users using botpress profiles +- [ ] Create endpoint to calculate the total volume traded +- [ ] Integrate Privy APIs to sign tx +- [ ] Get wallet balanced through Privy +- [ ] Remove Timeframe for strategies +- [ ] Update bot scenarios => shouldn't close current open position +- [ ] Remove accountName from getTicker +- [ ] Add enddate to backtest +- [ ] Replace WatchOnly by PaperTrading + +## Botpress : + +- [ ] Manage agent profiles +- [ ] Setup chat workflow +- [ ] Setup bad behaviors +- [ ] Setup chat for: create wallet, create strategy + +## N8N pipelines: + +- [ ] Create wallet: Check funds, approve usdc +- [ ] Create strategy: Get indicators, tickers and money management infos and call Managing.API +- [ ] Agent performance update + +## Infra : + +- [ ] Install botpress and n8n +- [ ] Setup botpress chat and language interpretation +- [ ] Add Tyk API Gateway for rate limit between n8n and Managing API +- [ ] New database Postgre for N8N and Botpress (agent profiles, built strategies, stats) + +_____________________________________________________________________________________________________________ # Front-end @@ -214,22 +256,14 @@ ________________________________________________________________________________ ## Trading desk -- [ ] WAIT - Create a button to open a hedge on option market - [ ] Get global exposure ((total position / total balances *) 100) +- [ ] WAIT - Create a button to open a hedge on option market - [ ] Display multiple tab to show different tickers -## Scenarios +## 3 - Settings [Link](https://github.com/users/CryptoOda/projects/1/views/1?pane=issue&itemId=32159287) -- [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 +- [ ] Save theme into database +- [ ] Call GET Settings/Theme to retrieve the theme used by the user ## Technical front-end : @@ -237,26 +271,17 @@ ________________________________________________________________________________ - [ ] 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 + - 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...) _____________________________________________________________________________________________________________ # Technicals +- [x] Store webapp into docker - [ ] 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 diff --git a/src/Managing.Api.Workers/Program.cs b/src/Managing.Api.Workers/Program.cs index 3818060..914adb1 100644 --- a/src/Managing.Api.Workers/Program.cs +++ b/src/Managing.Api.Workers/Program.cs @@ -18,6 +18,7 @@ using OpenApiSecurityScheme = NSwag.OpenApiSecurityScheme; // Builder var builder = WebApplication.CreateBuilder(args); +builder.WebHost.UseUrls("http://localhost:5001"); builder.Configuration.SetBasePath(AppContext.BaseDirectory); builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json"); diff --git a/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs b/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs index f9fa8f7..a0e60da 100644 --- a/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs +++ b/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs @@ -1,7 +1,10 @@ -using Managing.Application.Abstractions.Services; +using Managing.Application.Abstractions; +using Managing.Application.Abstractions.Services; using Managing.Application.Workers; using Managing.Application.Workers.Abstractions; +using Managing.Domain.Accounts; using Managing.Domain.Trades; +using Newtonsoft.Json; using static Managing.Common.Enums; namespace Managing.Api.Workers.Workers; @@ -13,13 +16,14 @@ public class PositionManagerWorker : BaseWorker private readonly IExchangeService _exchangeService; private readonly IAccountService _accountService; private readonly ILogger _logger; + private readonly ICacheService _cacheService; public PositionManagerWorker( ILogger logger, IWorkerService workerService, ITradingService tradingService, IExchangeService exchangeService, - IAccountService accountService) : base( + IAccountService accountService, ICacheService cacheService) : base( _workerType, logger, TimeSpan.FromMinutes(1), @@ -29,6 +33,7 @@ public class PositionManagerWorker : BaseWorker _tradingService = tradingService; _exchangeService = exchangeService; _accountService = accountService; + _cacheService = cacheService; } protected override async Task Run(CancellationToken cancellationToken) @@ -46,21 +51,23 @@ public class PositionManagerWorker : BaseWorker 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); + _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; + var stopLoss = await _exchangeService.OpenStopLoss(account, position.Ticker, + position.OriginDirection, + position.StopLoss.Price, position.StopLoss.Quantity, false, DateTime.UtcNow); if (stopLoss != null & (stopLoss.Status == TradeStatus.Requested)) { position.StopLoss = stopLoss; - _logger.LogInformation("|_ SL is requested"); + _logger.LogInformation("|_ SL is requested"); } else { @@ -74,7 +81,9 @@ public class PositionManagerWorker : BaseWorker 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; + var takeProfit1 = await _exchangeService.OpenTakeProfit(account, position.Ticker, + position.OriginDirection, position.TakeProfit1.Price, position.TakeProfit1.Quantity, false, + DateTime.UtcNow); if (takeProfit1 != null & (takeProfit1.Status == TradeStatus.Requested)) { @@ -94,7 +103,9 @@ public class PositionManagerWorker : BaseWorker 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; + var takeProfit2 = await _exchangeService.OpenTakeProfit(account, position.Ticker, + position.OriginDirection, position.TakeProfit2.Price, position.TakeProfit2.Quantity, false, + DateTime.UtcNow); if (takeProfit2 != null & (takeProfit2.Status == TradeStatus.Requested)) { @@ -116,9 +127,9 @@ public class PositionManagerWorker : BaseWorker _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)) + TradeStatus[] validStatus = [TradeStatus.Requested, TradeStatus.Filled]; + if (validStatus.Contains(position.StopLoss.Status) + && (validStatus.Contains(position.TakeProfit1.Status))) { position.Status = PositionStatus.Filled; _logger.LogInformation($"|_ Position is now open and SL/TP are correctly requested"); @@ -136,8 +147,9 @@ public class PositionManagerWorker : BaseWorker 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); + _logger.LogInformation("Managing filled position: {0} - Date: {1} - Direction: {2} - Ticker: {3}", + position.Identifier, position.Date, position.OriginDirection, position.Ticker); + var account = await GetAccount(position.AccountName); var updatedPosition = await _tradingService.ManagePosition(account, position); _tradingService.UpdatePosition(updatedPosition); @@ -158,9 +170,10 @@ public class PositionManagerWorker : BaseWorker foreach (var position in positions) { - _logger.LogInformation("Managing position: {0} - Date: {1} - Direction: {2} - Ticker: {3}", position.Identifier, position.Date, position.OriginDirection, position.Ticker); + _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); + _tradingService.UpdatePosition(position); // Update status if position is open since to long if (position.Date < DateTime.UtcNow.AddDays(-2)) @@ -171,14 +184,15 @@ public class PositionManagerWorker : BaseWorker 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 account = await GetAccount(position.AccountName); + var currentOpenOrders = await _exchangeService.GetOpenOrders(account, position.Ticker); + // if (currentOpenOrders.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); @@ -198,4 +212,16 @@ public class PositionManagerWorker : BaseWorker } } -} + private async Task GetAccount(string accountName) + { + var account = _cacheService.GetValue(accountName); + + if (account == null) + { + account = await _accountService.GetAccount(accountName, false, false); + _cacheService.SaveValue(accountName, JsonConvert.SerializeObject(account)); + } + + return account; + } +} \ No newline at end of file diff --git a/src/Managing.Api/Controllers/TradingController.cs b/src/Managing.Api/Controllers/TradingController.cs index 730abc8..04621c6 100644 --- a/src/Managing.Api/Controllers/TradingController.cs +++ b/src/Managing.Api/Controllers/TradingController.cs @@ -39,13 +39,14 @@ public class TradingController : ControllerBase ICommandHandler openTradeCommandHandler, ICommandHandler closeTradeCommandHandler, ITradingService tradingService, - IMediator mediator) + IMediator mediator, IMoneyManagementService moneyManagementService) { _logger = logger; _openTradeCommandHandler = openTradeCommandHandler; _closeTradeCommandHandler = closeTradeCommandHandler; _tradingService = tradingService; _mediator = mediator; + _moneyManagementService = moneyManagementService; } /// @@ -112,7 +113,7 @@ public class TradingController : ControllerBase /// The money management strategy details (optional). /// The opening price for the trade (optional). /// The opened position. - [HttpGet("OpenPosition")] + [HttpPost("OpenPosition")] public async Task> Trade( string accountName, string moneyManagementName, @@ -120,7 +121,7 @@ public class TradingController : ControllerBase Ticker ticker, RiskLevel riskLevel, bool isForPaperTrading, - MoneyManagement? moneyManagement = null, + MoneyManagement? moneyManagement = null, decimal? openPrice = null) { if (string.IsNullOrEmpty(accountName)) @@ -130,10 +131,11 @@ public class TradingController : ControllerBase if (string.IsNullOrEmpty(moneyManagementName) && moneyManagement == null) { - throw new ArgumentException($"'{nameof(moneyManagementName)}' cannot be null or empty.", nameof(moneyManagementName)); + throw new ArgumentException($"'{nameof(moneyManagementName)}' cannot be null or empty.", + nameof(moneyManagementName)); } - if (moneyManagement == null) + if (moneyManagement != null) { moneyManagement = await _moneyManagementService.GetMoneyMangement(moneyManagementName); } diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs index 6f1b54b..bfb6eff 100644 --- a/src/Managing.Application/Bots/TradingBot.cs +++ b/src/Managing.Application/Bots/TradingBot.cs @@ -308,11 +308,14 @@ public class TradingBot : Bot, ITradingBot { await HandleClosedPosition(positionForSignal); } - else if (position.Status == PositionStatus.Filled) + else if (position.Status == (PositionStatus.Filled | PositionStatus.PartiallyFilled)) { // 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 + + // Position might be partially filled, meaning that TPSL havent been sended yet + // But the position might already been closed by the exchange so we have to check should be closed var lastCandle = IsForBacktest ? OptimizedCandles.Last() : ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow); diff --git a/src/Managing.Infrastructure.Web3/EvmManager.cs b/src/Managing.Infrastructure.Web3/EvmManager.cs index 7fd782c..6409170 100644 --- a/src/Managing.Infrastructure.Web3/EvmManager.cs +++ b/src/Managing.Infrastructure.Web3/EvmManager.cs @@ -618,7 +618,7 @@ public class EvmManager : IEvmManager { var chain = ChainService.GetChain(chainName); var web3 = new Web3(chain.RpcUrl); - var quantity = await GmxService.QuantityInPosition(web3, publicAddress, ticker); + var quantity = await _gmxV2Service.QuantityInPosition(web3, publicAddress, ticker); return quantity; } diff --git a/src/Managing.Infrastructure.Web3/Services/Gmx/GmxV2Service.cs b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxV2Service.cs index b2cf200..4a7877d 100644 --- a/src/Managing.Infrastructure.Web3/Services/Gmx/GmxV2Service.cs +++ b/src/Managing.Infrastructure.Web3/Services/Gmx/GmxV2Service.cs @@ -1372,4 +1372,9 @@ public class GmxV2Service { return $"{account}-{market}-{collateralToken}-{(isLong ? "true" : "false")}"; } + + public async Task QuantityInPosition(Web3 web3, string publicAddress, Enums.Ticker ticker) + { + return (await GetTrade(web3, publicAddress, ticker)).Status == Enums.TradeStatus.Filled ? 1 : 0; + } } \ No newline at end of file diff --git a/src/Managing.WebApp/src/pages/desk/deskWidget.tsx b/src/Managing.WebApp/src/pages/desk/deskWidget.tsx index a2c8784..038a17d 100644 --- a/src/Managing.WebApp/src/pages/desk/deskWidget.tsx +++ b/src/Managing.WebApp/src/pages/desk/deskWidget.tsx @@ -9,7 +9,11 @@ const loadWidget = (component: any) => { const DeskWidget = React.forwardRef((props: any, ref) => { const widget = props const widgetProperties = widget['data-properties'] as IWidgetProperties - const WidgetComponent = loadWidget(widgetProperties.id) + + if (!widgetProperties) { + return null + } + const WidgetComponent = loadWidget(widgetProperties?.id) return (
Loading}> diff --git a/src/Managing.WebApp/src/pages/desk/widgets/OpenPositionWidget.tsx b/src/Managing.WebApp/src/pages/desk/widgets/OpenPositionWidget.tsx index 7a33197..1452a3e 100644 --- a/src/Managing.WebApp/src/pages/desk/widgets/OpenPositionWidget.tsx +++ b/src/Managing.WebApp/src/pages/desk/widgets/OpenPositionWidget.tsx @@ -47,7 +47,7 @@ const OpenPositonWidget = () => { form.riskLevel, false, form.stopLoss, - form.takeProfit + undefined ) .then((result) => { t.update('success', 'Position created')