public async Task Handle(FailLiquidationInternalCommand command, IEventPublisher publisher) { var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>( operationName : LiquidationSaga.OperationName, id : command.OperationId); if (executionInfo?.Data == null) { return; } _accountUpdateService.RemoveLiquidationStateIfNeeded(executionInfo.Data.AccountId, $"Liquidation [{command.OperationId}] failed ({command.Reason})", command.OperationId, executionInfo.Data.LiquidationType); _chaosKitty.Meow( $"{nameof(FailLiquidationInternalCommand)}:" + $"Publish_LiquidationFailedInternalEvent:" + $"{command.OperationId}"); publisher.PublishEvent(new LiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = command.Reason, LiquidationType = command.LiquidationType.ToType <LiquidationTypeContract>(), AccountId = executionInfo.Data.AccountId, AssetPairId = executionInfo.Data.AssetPairId, Direction = executionInfo.Data.Direction?.ToType <PositionDirectionContract>(), QuoteInfo = executionInfo.Data.QuoteInfo, ProcessedPositionIds = executionInfo.Data.ProcessedPositionIds, LiquidatedPositionIds = executionInfo.Data.LiquidatedPositionIds, OpenPositionsRemainingOnAccount = _ordersCache.Positions.GetPositionsByAccountIds(executionInfo.Data.AccountId).Count, CurrentTotalCapital = _accountsCache.Get(executionInfo.Data.AccountId).GetTotalCapital(), }); _liquidationEndEventChannel.SendEvent(this, new LiquidationEndEventArgs { OperationId = command.OperationId, CreationTime = _dateService.Now(), AccountId = executionInfo.Data.AccountId, LiquidatedPositionIds = executionInfo.Data.LiquidatedPositionIds, FailReason = command.Reason, }); }
public async Task ExecuteAsync(IEventPublisher failurePublisher, string accountId, string operationId, string reason) { if (failurePublisher == null) { throw new ArgumentNullException(nameof(failurePublisher)); } if (string.IsNullOrEmpty(accountId)) { throw new ArgumentNullException(nameof(accountId)); } if (string.IsNullOrEmpty(operationId)) { throw new ArgumentNullException(nameof(operationId)); } var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>( LiquidationSaga.OperationName, operationId); if (executionInfo?.Data == null) { await _log.WriteWarningAsync(nameof(LiquidationFailureExecutor), nameof(ExecuteAsync), new { operationId, accountId }.ToJson(), $"Unable to execute failure. Liquidation execution info was not found."); return; } _accountUpdateService.RemoveLiquidationStateIfNeeded(accountId, reason, operationId, executionInfo.Data.LiquidationType); var account = _accountsCache.Get(accountId); failurePublisher.PublishEvent(new LiquidationFailedEvent { OperationId = operationId, CreationTime = _dateService.Now(), Reason = reason, LiquidationType = executionInfo.Data.LiquidationType.ToType <LiquidationTypeContract>(), AccountId = executionInfo.Data.AccountId, AssetPairId = executionInfo.Data.AssetPairId, Direction = executionInfo.Data.Direction?.ToType <PositionDirectionContract>(), QuoteInfo = executionInfo.Data.QuoteInfo, ProcessedPositionIds = executionInfo.Data.ProcessedPositionIds, LiquidatedPositionIds = executionInfo.Data.LiquidatedPositionIds, OpenPositionsRemainingOnAccount = _ordersCache.Positions.GetPositionsByAccountIds(executionInfo.Data.AccountId).Count, CurrentTotalCapital = account.GetTotalCapital(), }); await _log.WriteInfoAsync(nameof(LiquidationFailureExecutor), nameof(ExecuteAsync), new { account, operationId, reason }.ToJson(), $"Successfully published {nameof(LiquidationFailedEvent)} event"); _liquidationEndEventChannel.SendEvent(this, new LiquidationEndEventArgs { OperationId = operationId, CreationTime = _dateService.Now(), AccountId = executionInfo.Data.AccountId, LiquidatedPositionIds = executionInfo.Data.LiquidatedPositionIds, FailReason = reason, }); await _log.WriteInfoAsync(nameof(LiquidationFailureExecutor), nameof(ExecuteAsync), new { account, operationId, reason }.ToJson(), $"Successfully published {nameof(LiquidationEndEventArgs)} event"); }
public async Task Handle(AccountChangedEvent e) { var executionInfo = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : OperationName, operationId : e.OperationId, factory : () => new OperationExecutionInfo <OperationData>( operationName: OperationName, id: e.OperationId, lastModified: _dateService.Now(), data: new OperationData { State = OperationState.Initiated } )); if (executionInfo.Data.SwitchState(OperationState.Initiated, OperationState.Finished)) { var updatedAccount = Convert(e.Account); switch (e.EventType) { case AccountChangedEventTypeContract.Created: _accountsCacheService.TryAddNew(MarginTradingAccount.Create(updatedAccount)); break; case AccountChangedEventTypeContract.Updated: { var account = _accountsCacheService.TryGet(e.Account.Id); if (await ValidateAccount(account, e) && await _accountsCacheService.UpdateAccountChanges(updatedAccount.Id, updatedAccount.TradingConditionId, updatedAccount.WithdrawTransferLimit, updatedAccount.IsDisabled, updatedAccount.IsWithdrawalDisabled, e.ChangeTimestamp)) { _accountUpdateService.RemoveLiquidationStateIfNeeded(e.Account.Id, "Trading conditions changed"); } break; } case AccountChangedEventTypeContract.BalanceUpdated: { if (e.BalanceChange != null) { var account = _accountsCacheService.TryGet(e.Account.Id); if (await ValidateAccount(account, e)) { switch (e.BalanceChange.ReasonType) { case AccountBalanceChangeReasonTypeContract.Withdraw: await _accountUpdateService.UnfreezeWithdrawalMargin(updatedAccount.Id, e.BalanceChange.Id); break; case AccountBalanceChangeReasonTypeContract.UnrealizedDailyPnL: if (_ordersCache.Positions.TryGetPositionById(e.BalanceChange.EventSourceId, out var position)) { position.ChargePnL(e.BalanceChange.Id, e.BalanceChange.ChangeAmount); } else { _log.WriteWarning("AccountChangedEvent Handler", e.ToJson(), $"Position [{e.BalanceChange.EventSourceId} was not found]"); } break; case AccountBalanceChangeReasonTypeContract.RealizedPnL: await _accountUpdateService.UnfreezeUnconfirmedMargin(e.Account.Id, e.BalanceChange.EventSourceId); break; } if (await _accountsCacheService.UpdateAccountBalance(updatedAccount.Id, e.BalanceChange.Balance, e.ChangeTimestamp)) { _accountUpdateService.RemoveLiquidationStateIfNeeded(e.Account.Id, "Balance updated"); _accountBalanceChangedEventChannel.SendEvent(this, new AccountBalanceChangedEventArgs(updatedAccount)); } } } else { _log.WriteWarning("AccountChangedEvent Handler", e.ToJson(), "BalanceChange info is empty"); } break; } case AccountChangedEventTypeContract.Deleted: //account deletion from cache is double-handled by CQRS flow _accountsCacheService.Remove(e.Account.Id); break; default: await _log.WriteErrorAsync(nameof(AccountsProjection), nameof(AccountChangedEvent), e.ToJson(), new Exception("AccountChangedEventTypeContract was in incorrect state")); break; } _chaosKitty.Meow(e.OperationId); await _operationExecutionInfoRepository.Save(executionInfo); } }