private async Task Handle(FreezeAmountForWithdrawalCommand command, IEventPublisher publisher) { var(executionInfo, _) = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : OperationName, operationId : command.OperationId, factory : () => new OperationExecutionInfo <WithdrawalFreezeOperationData>( operationName: OperationName, id: command.OperationId, lastModified: _dateService.Now(), data: new WithdrawalFreezeOperationData { State = OperationState.Initiated, AccountId = command.AccountId, Amount = command.Amount, } )); MarginTradingAccount account = null; try { account = _accountsCacheService.Get(command.AccountId); } catch { publisher.PublishEvent(new AmountForWithdrawalFreezeFailedEvent(command.OperationId, _dateService.Now(), command.AccountId, command.Amount, $"Failed to get account {command.AccountId}")); return; } if (executionInfo.Data.SwitchState(OperationState.Initiated, OperationState.Started)) { if (account.GetFreeMargin() >= command.Amount) { await _accountUpdateService.FreezeWithdrawalMargin(command.AccountId, command.OperationId, command.Amount); _chaosKitty.Meow(command.OperationId); publisher.PublishEvent(new AmountForWithdrawalFrozenEvent(command.OperationId, _dateService.Now(), command.AccountId, command.Amount, command.Reason)); } else { publisher.PublishEvent(new AmountForWithdrawalFreezeFailedEvent(command.OperationId, _dateService.Now(), command.AccountId, command.Amount, "Not enough free margin")); } _chaosKitty.Meow(command.OperationId); await _operationExecutionInfoRepository.Save(executionInfo); } }
private async Task Handle(WithdrawCommand command, IEventPublisher publisher) { await _executionInfoRepository.GetOrAddAsync( OperationName, command.OperationId, () => new OperationExecutionInfo <WithdrawalDepositData>( OperationName, command.OperationId, new WithdrawalDepositData { AccountId = command.AccountId, Amount = command.Amount, AuditLog = command.AuditLog, State = WithdrawalState.Created, Comment = command.Comment }, _systemClock.UtcNow.UtcDateTime)); var account = await _accountsRepository.GetAsync(command.AccountId); if (account == null) { publisher.PublishEvent(new WithdrawalStartFailedInternalEvent(command.OperationId, _systemClock.UtcNow.UtcDateTime, $"Account {command.AccountId} not found.")); return; } var accountCapital = await _accountManagementService.GetAccountCapitalAsync(account.Id, useCache : false); if (accountCapital.Disposable < command.Amount) { publisher.PublishEvent(new WithdrawalStartFailedInternalEvent(command.OperationId, _systemClock.UtcNow.UtcDateTime, $"Account {account.Id} balance {accountCapital.Balance}{accountCapital.AssetId} is not enough to withdraw {command.Amount}{accountCapital.AssetId}. " + $"Taking into account the current state of the trading account: {accountCapital.ToJson()}.")); return; } if (account.IsWithdrawalDisabled) { publisher.PublishEvent(new WithdrawalStartFailedInternalEvent(command.OperationId, _systemClock.UtcNow.UtcDateTime, "Withdrawal is disabled")); return; } _chaosKitty.Meow(command.OperationId); publisher.PublishEvent(new WithdrawalStartedInternalEvent(command.OperationId, _systemClock.UtcNow.UtcDateTime)); }
private async Task Handle(DepositCommand c, IEventPublisher publisher) { await _executionInfoRepository.GetOrAddAsync( OperationName, c.OperationId, () => new OperationExecutionInfo <WithdrawalDepositData>( OperationName, c.OperationId, new WithdrawalDepositData { AccountId = c.AccountId, Amount = c.Amount, AuditLog = c.AuditLog, State = WithdrawalState.Created, Comment = c.Comment }, _systemClock.UtcNow.UtcDateTime)); _chaosKitty.Meow(c.OperationId); publisher.PublishEvent(new DepositStartedInternalEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime)); }
private async Task Handle(BlockAccountsForDeletionCommand command, IEventPublisher publisher) { var executionInfo = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : OperationName, operationId : command.OperationId, factory : () => new OperationExecutionInfo <DeleteAccountsOperationData>( operationName: OperationName, id: command.OperationId, lastModified: _dateService.Now(), data: new DeleteAccountsOperationData { State = OperationState.Initiated } )); //todo think how to remove state saving from commands handler here and for Withdrawal if (executionInfo.Data.SwitchState(OperationState.Initiated, OperationState.Started)) { var failedAccounts = new Dictionary <string, string>(); foreach (var accountId in command.AccountIds) { MarginTradingAccount account = null; try { account = _accountsCacheService.Get(accountId); } catch (Exception exception) { failedAccounts.Add(accountId, exception.Message); continue; } var positionsCount = _orderReader.GetPositions().Count(x => x.AccountId == accountId); if (positionsCount != 0) { failedAccounts.Add(accountId, $"Account contain {positionsCount} open positions which must be closed before account deletion."); continue; } var orders = _orderReader.GetPending().Where(x => x.AccountId == accountId).ToList(); if (orders.Any()) { var(failedToCloseOrderId, failReason) = ((string)null, (string)null); foreach (var order in orders) { try { _tradingEngine.CancelPendingOrder(order.Id, order.AdditionalInfo, command.OperationId, $"{nameof(DeleteAccountsCommandsHandler)}: force close all orders.", OrderCancellationReason.AccountInactivated); } catch (Exception exception) { failedToCloseOrderId = order.Id; failReason = exception.Message; break; } } if (failedToCloseOrderId != null) { failedAccounts.Add(accountId, $"Failed to close order [{failedToCloseOrderId}]: {failReason}."); continue; } } if (account.AccountFpl.WithdrawalFrozenMarginData.Any()) { await _log.WriteErrorAsync(nameof(DeleteAccountsCommandsHandler), nameof(BlockAccountsForDeletionCommand), account.ToJson(), new Exception("While deleting an account it contained some frozen withdrawal data. Account is deleted.")); } if (account.AccountFpl.UnconfirmedMarginData.Any()) { await _log.WriteErrorAsync(nameof(DeleteAccountsCommandsHandler), nameof(BlockAccountsForDeletionCommand), account.ToJson(), new Exception("While deleting an account it contained some unconfirmed margin data. Account is deleted.")); } if (account.Balance != 0) { await _log.WriteErrorAsync(nameof(DeleteAccountsCommandsHandler), nameof(BlockAccountsForDeletionCommand), account.ToJson(), new Exception("While deleting an account it's balance on side of TradingCore was non zero. Account is deleted.")); } if (!await UpdateAccount(account, true, r => failedAccounts.Add(accountId, r), command.Timestamp)) { continue; } } publisher.PublishEvent(new AccountsBlockedForDeletionEvent( operationId: command.OperationId, eventTimestamp: _dateService.Now(), failedAccountIds: failedAccounts )); _chaosKitty.Meow($"{nameof(BlockAccountsForDeletionCommand)}: " + "Save_OperationExecutionInfo: " + $"{command.OperationId}"); await _operationExecutionInfoRepository.Save(executionInfo); } }
public async Task Handle(StartLiquidationInternalCommand command, IEventPublisher publisher) { #region Private Methods void PublishFailedEvent(string reason) { publisher.PublishEvent(new LiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = reason, LiquidationType = command.LiquidationType.ToType <LiquidationTypeContract>(), AccountId = command.AccountId, AssetPairId = command.AssetPairId, Direction = command.Direction?.ToType <PositionDirectionContract>(), }); _liquidationEndEventChannel.SendEvent(this, new LiquidationEndEventArgs { OperationId = command.OperationId, CreationTime = _dateService.Now(), AccountId = command.AccountId, LiquidatedPositionIds = new List <string>(), FailReason = reason, }); } #endregion #region Validations if (string.IsNullOrEmpty(command.AccountId)) { PublishFailedEvent("AccountId must be specified"); return; } if (_accountsCache.TryGet(command.AccountId) == null) { PublishFailedEvent("Account does not exist"); return; } #endregion var(executionInfo, _) = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : LiquidationSaga.OperationName, operationId : command.OperationId, factory : () => new OperationExecutionInfo <LiquidationOperationData>( operationName: LiquidationSaga.OperationName, id: command.OperationId, lastModified: _dateService.Now(), data: new LiquidationOperationData { State = LiquidationOperationState.Initiated, AccountId = command.AccountId, AssetPairId = command.AssetPairId, QuoteInfo = command.QuoteInfo, Direction = command.Direction, LiquidatedPositionIds = new List <string>(), ProcessedPositionIds = new List <string>(), LiquidationType = command.LiquidationType, OriginatorType = command.OriginatorType, AdditionalInfo = command.AdditionalInfo, StartedAt = command.CreationTime } )); if (executionInfo.Data.State == LiquidationOperationState.Initiated) { if (!_accountsCache.TryStartLiquidation(command.AccountId, command.OperationId, out var currentOperationId)) { if (currentOperationId != command.OperationId) { PublishFailedEvent( $"Liquidation is already in progress. Initiated by operation : {currentOperationId}"); return; } } _chaosKitty.Meow( $"{nameof(StartLiquidationInternalCommand)}:" + $"Publish_LiquidationStartedInternalEvent:" + $"{command.OperationId}"); publisher.PublishEvent(new LiquidationStartedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), AssetPairId = executionInfo.Data.AssetPairId, AccountId = executionInfo.Data.AccountId, LiquidationType = executionInfo.Data.LiquidationType.ToType <LiquidationTypeContract>() }); } }
public async Task Handle(StartRevokeTemporaryCapitalInternalCommand c, IEventPublisher publisher) { var executionInfo = await _executionInfoRepository.GetOrAddAsync( OperationName, c.OperationId, () => new OperationExecutionInfo <RevokeTemporaryCapitalData>( OperationName, c.OperationId, new RevokeTemporaryCapitalData { State = TemporaryCapitalState.Initiated, OperationId = c.OperationId, AccountId = c.AccountId, RevokeEventSourceId = c.RevokeEventSourceId, Comment = c.Comment, AdditionalInfo = c.AdditionalInfo, }, _systemClock.UtcNow.UtcDateTime)); if (executionInfo.Data.State != TemporaryCapitalState.Initiated) { return; } var account = await _accountsRepository.GetAsync(c.AccountId); if (account == null) { publisher.PublishEvent(new RevokeTemporaryCapitalFailedEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, $"Account {c.AccountId} not found", c.RevokeEventSourceId)); return; } if (!string.IsNullOrEmpty(c.RevokeEventSourceId) && account.TemporaryCapital.All(x => x.Id != c.RevokeEventSourceId)) { publisher.PublishEvent(new RevokeTemporaryCapitalFailedEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, $"Account {c.AccountId} doesn't contain temporary capital with id {c.RevokeEventSourceId}", c.RevokeEventSourceId)); return; } var temporaryCapitalToRevoke = account.TemporaryCapital .Where(x => string.IsNullOrEmpty(c.RevokeEventSourceId) || x.Id == c.RevokeEventSourceId) .ToList(); var accountCapital = await _accountManagementService.GetAccountCapitalAsync(account.Id, useCache : false); var amountToRevoke = temporaryCapitalToRevoke.Sum(x => x.Amount); if (accountCapital.CanRevokeAmount < amountToRevoke) { publisher.PublishEvent(new RevokeTemporaryCapitalFailedEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, $"Account {c.AccountId} balance {account.Balance}{account.BaseAssetId} is not enough to revoke {amountToRevoke}{account.BaseAssetId}. Taking into account the current state of the trading account: {accountCapital.ToJson()}.", c.RevokeEventSourceId)); return; } var accountStat = await _accountsApi.GetAccountStats(c.AccountId); if (accountStat != null && accountStat.FreeMargin < amountToRevoke) { publisher.PublishEvent(new RevokeTemporaryCapitalFailedEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, $"MT Core account {c.AccountId} free margin {accountStat.FreeMargin}{account.BaseAssetId} is not enough to revoke {amountToRevoke}{account.BaseAssetId}.", c.RevokeEventSourceId)); return; } try { await _accountsRepository.UpdateAccountTemporaryCapitalAsync(c.AccountId, AccountManagementService.UpdateTemporaryCapital, string.IsNullOrEmpty(c.RevokeEventSourceId) ?null : new TemporaryCapital { Id = c.RevokeEventSourceId }, false); } catch (Exception exception) { await _log.WriteErrorAsync(nameof(RevokeTemporaryCapitalCommandsHandler), nameof(StartRevokeTemporaryCapitalInternalCommand), exception); publisher.PublishEvent(new RevokeTemporaryCapitalFailedEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, exception.Message, c.RevokeEventSourceId)); return; } _chaosKitty.Meow( $"{nameof(StartRevokeTemporaryCapitalInternalCommand)}: " + "publisher.PublishEvent: " + $"{c.OperationId}"); publisher.PublishEvent(new RevokeTemporaryCapitalStartedInternalEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, temporaryCapitalToRevoke)); }
private async Task Handle(StartSpecialLiquidationInternalCommand command, IEventPublisher publisher) { if (!_marginTradingSettings.SpecialLiquidation.Enabled) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = "Special liquidation is disabled in settings", }); return; } //validate the list of positions contain only the same instrument var positions = _orderReader.GetPositions().Where(x => command.PositionIds.Contains(x.Id)).ToList(); if (!string.IsNullOrEmpty(command.AccountId)) { if (_accountsCacheService.TryGet(command.AccountId) == null) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = $"Account {command.AccountId} does not exist", }); return; } positions = positions.Where(x => x.AccountId == command.AccountId).ToList(); } if (positions.Select(x => x.AssetPairId).Distinct().Count() > 1) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = "The list of positions is of different instruments", }); return; } if (!positions.Any()) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = "No positions to liquidate", }); return; } if (!TryGetExchangeNameFromPositions(positions, out var externalProviderId)) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = "All requested positions must be open on the same external exchange", }); return; } var assetPairId = positions.First().AssetPairId; if (_assetPairsCache.GetAssetPairById(assetPairId).IsDiscontinued) { publisher.PublishEvent(new SpecialLiquidationFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = $"Asset pair {assetPairId} is discontinued", }); return; } var executionInfo = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : SpecialLiquidationSaga.OperationName, operationId : command.OperationId, factory : () => new OperationExecutionInfo <SpecialLiquidationOperationData>( operationName: SpecialLiquidationSaga.OperationName, id: command.OperationId, lastModified: _dateService.Now(), data: new SpecialLiquidationOperationData { State = SpecialLiquidationOperationState.Initiated, Instrument = assetPairId, PositionIds = positions.Select(x => x.Id).ToList(), ExternalProviderId = externalProviderId, AccountId = command.AccountId, CausationOperationId = command.CausationOperationId, AdditionalInfo = command.AdditionalInfo, OriginatorType = command.OriginatorType } )); if (executionInfo.Data.SwitchState(SpecialLiquidationOperationState.Initiated, SpecialLiquidationOperationState.Started)) { publisher.PublishEvent(new SpecialLiquidationStartedInternalEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Instrument = positions.FirstOrDefault()?.AssetPairId, }); _chaosKitty.Meow(command.OperationId); await _operationExecutionInfoRepository.Save(executionInfo); } }
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); } }
private async Task Handle(UpdateBalanceInternalCommand command, IEventPublisher publisher) { var executionInfo = await _executionInfoRepository.GetOrAddAsync( OperationName, command.OperationId, () => new OperationExecutionInfo<OperationData>( OperationName, command.OperationId, new OperationData { State = OperationState.Created }, _systemClock.UtcNow.UtcDateTime)); if (SwitchState(executionInfo.Data, OperationState.Created, OperationState.Started)) { IAccount account = null; try { account = await _accountsRepository.UpdateBalanceAsync( command.OperationId, command.AccountId, command.AmountDelta, false); } catch (ValidationException ex) { await _log.WriteWarningAsync(nameof(UpdateBalanceCommandsHandler), nameof(UpdateBalanceInternalCommand), ex.Message); publisher.PublishEvent(new AccountBalanceChangeFailedEvent(command.OperationId, _systemClock.UtcNow.UtcDateTime, ex.Message, command.Source)); await _executionInfoRepository.SaveAsync(executionInfo); return; } _chaosKitty.Meow(command.OperationId); var change = new AccountBalanceChangeContract( command.OperationId, account.ModificationTimestamp, account.Id, account.ClientId, command.AmountDelta, account.Balance, account.WithdrawTransferLimit, command.Comment, Convert(command.ChangeReasonType), command.EventSourceId, account.LegalEntity, command.AuditLog, command.AssetPairId, command.TradingDay); var convertedAccount = Convert(account); publisher.PublishEvent( new AccountChangedEvent( change.ChangeTimestamp, command.Source, convertedAccount, AccountChangedEventTypeContract.BalanceUpdated, change, command.OperationId) ); await _executionInfoRepository.SaveAsync(executionInfo); } }
private async Task Handle(DeleteAccountsCommand command, IEventPublisher publisher) { if (string.IsNullOrWhiteSpace(command.OperationId)) { command.OperationId = Guid.NewGuid().ToString("N"); } if (command.AccountIds == null || !command.AccountIds.Any()) { publisher.PublishEvent(new AccountsDeletionFinishedEvent( command.OperationId, _systemClock.UtcNow.UtcDateTime, new List <string>(), new Dictionary <string, string>(), command.Comment )); return; } command.AccountIds = command.AccountIds.Distinct().ToList(); var executionInfo = await _executionInfoRepository.GetOrAddAsync( OperationName, command.OperationId, () => new OperationExecutionInfo <DeleteAccountsData>( OperationName, command.OperationId, new DeleteAccountsData { State = DeleteAccountsState.Initiated, OperationId = command.OperationId, AccountIds = command.AccountIds, Comment = command.Comment, }, _systemClock.UtcNow.UtcDateTime)); if (executionInfo.Data.State != DeleteAccountsState.Initiated) { return; } var failedAccounts = await ValidateAccountsAsync(executionInfo.Data.AccountIds); foreach (var accountToBlock in command.AccountIds.Except(failedAccounts.Keys)) { try { var account = (await _accountsRepository.GetAsync(accountToBlock)) .RequiredNotNull(nameof(accountToBlock), $"Account {accountToBlock} does not exist."); var result = await _accountsRepository.UpdateAccountAsync( accountToBlock, true, true); _eventSender.SendAccountChangedEvent( nameof(DeleteAccountsCommand), result, AccountChangedEventTypeContract.Updated, $"{command.OperationId}_{accountToBlock}", previousSnapshot: account); } catch (Exception exception) { await _log.WriteErrorAsync(nameof(DeleteAccountsCommandsHandler), nameof(DeleteAccountsCommand), exception); failedAccounts.Add(accountToBlock, exception.Message); } } if (!command.AccountIds.Except(failedAccounts.Keys).Any()) { publisher.PublishEvent(new AccountsDeletionFinishedEvent( command.OperationId, _systemClock.UtcNow.UtcDateTime, new List <string>(), failedAccounts, executionInfo.Data.Comment )); return; } _chaosKitty.Meow($"{nameof(DeleteAccountsCommand)}: " + "DeleteAccountsStartedInternalEvent: " + $"{command.OperationId}"); publisher.PublishEvent(new DeleteAccountsStartedInternalEvent( command.OperationId, _systemClock.UtcNow.UtcDateTime, failedAccounts )); }
public async Task Handle(StartGiveTemporaryCapitalInternalCommand c, IEventPublisher publisher) { var executionInfo = await _executionInfoRepository.GetOrAddAsync( OperationName, c.OperationId, () => new OperationExecutionInfo <GiveTemporaryCapitalData>( OperationName, c.OperationId, new GiveTemporaryCapitalData { State = TemporaryCapitalState.Initiated, OperationId = c.OperationId, AccountId = c.AccountId, Amount = c.Amount, Reason = c.Reason, Comment = c.Comment, AdditionalInfo = c.AdditionalInfo, }, _systemClock.UtcNow.UtcDateTime)); if (executionInfo.Data.State != TemporaryCapitalState.Initiated) { return; } _chaosKitty.Meow(c.OperationId); var account = await _accountsRepository.GetAsync(c.AccountId); if (account == null) { publisher.PublishEvent(new GiveTemporaryCapitalFailedEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, $"Account {c.AccountId} not found")); return; } if (account.TemporaryCapital.Any(x => x.Id == c.OperationId)) { publisher.PublishEvent(new GiveTemporaryCapitalStartedInternalEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime)); return; } try { await _accountsRepository.UpdateAccountTemporaryCapitalAsync(c.AccountId, AccountManagementService.UpdateTemporaryCapital, new InternalModels.TemporaryCapital { Id = c.OperationId, Amount = c.Amount, }, true); } catch (Exception exception) { await _log.WriteErrorAsync(nameof(GiveTemporaryCapitalCommandsHandler), nameof(StartGiveTemporaryCapitalInternalCommand), exception); publisher.PublishEvent(new GiveTemporaryCapitalFailedEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime, exception.Message)); return; } _chaosKitty.Meow($"{nameof(StartGiveTemporaryCapitalInternalCommand)}: " + "publisher.PublishEvent: " + c.OperationId); publisher.PublishEvent(new GiveTemporaryCapitalStartedInternalEvent(c.OperationId, _systemClock.UtcNow.UtcDateTime)); }