public override Task Execute() { var pendingOrders = _orderReader.GetPending().GroupBy(o => o.Instrument); foreach (var gr in pendingOrders) { if (!_assetDayOffService.ArePendingOrdersDisabled(gr.Key)) { continue; } foreach (var pendingOrder in gr) { try { _tradingEngine.CancelPendingOrder(pendingOrder.Id, OrderCloseReason.CanceledBySystem); } catch (Exception e) { _log.WriteErrorAsync(nameof(PendingOrdersCleaningService), $"Cancelling pending order {pendingOrder.Id}", pendingOrder.ToJson(), e); } } } return(Task.CompletedTask); }
public Task CancelAsync(string orderId, [FromBody] OrderCancelRequest request = null, string accountId = null) { if (!_ordersCache.TryGetOrderById(orderId, out var order)) { throw new InvalidOperationException("Order not found"); } ValidationHelper.ValidateAccountId(order, accountId); var reason = request?.Originator == OriginatorTypeContract.System ? OrderCancellationReason.CorporateAction : OrderCancellationReason.None; var canceledOrder = _tradingEngine.CancelPendingOrder(order.Id, request?.AdditionalInfo, request?.Comment, reason); _operationsLogService.AddLog("action order.cancel", order.AccountId, request?.ToJson(), canceledOrder.ToJson()); return(Task.CompletedTask); }
public async Task <List <IOrder> > CloseAccountOrders(string accountId) { var openedOrders = _ordersCache.ActiveOrders.GetOrdersByAccountIds(accountId).ToArray(); var closedOrders = new List <IOrder>(); foreach (var order in openedOrders) { try { var closedOrder = await _tradingEngine.CloseActiveOrderAsync(order.Id, OrderCloseReason.ClosedByBroker, "Close orders for account"); closedOrders.Add(closedOrder); } catch (Exception e) { await _log.WriteWarningAsync(nameof(AccountManager), "CloseAccountActiveOrders", $"AccountId: {accountId}, OrderId: {order.Id}", $"Error closing order: {e.Message}"); } } var pendingOrders = _ordersCache.WaitingForExecutionOrders.GetOrdersByAccountIds(accountId); foreach (var order in pendingOrders) { try { var closedOrder = _tradingEngine.CancelPendingOrder(order.Id, OrderCloseReason.CanceledByBroker, "Close orders for account"); closedOrders.Add(closedOrder); } catch (Exception e) { await _log.WriteWarningAsync(nameof(AccountManager), "CloseAccountOrders", $"AccountId: {accountId}, OrderId: {order.Id}", $"Error cancelling order: {e.Message}"); } } return(closedOrders); }
public async Task <BackendResponse <bool> > CancelOrder([FromBody] CloseOrderBackendRequest request) { if (!_ordersCache.WaitingForExecutionOrders.TryGetOrderById(request.OrderId, out var order)) { return(BackendResponse <bool> .Error("Order not found")); } if (_assetDayOffService.IsDayOff(order.Instrument)) { return(BackendResponse <bool> .Error("Trades for instrument are not available")); } if (order.ClientId != request.ClientId || order.AccountId != request.AccountId) { return(BackendResponse <bool> .Error("Order is not available for user")); } if (request.IsForcedByBroker && string.IsNullOrEmpty(request.Comment)) { return(BackendResponse <bool> .Error("For operation forced by broker, comment is mandatory")); } var reason = request.IsForcedByBroker ? OrderCloseReason.CanceledByBroker : OrderCloseReason.Canceled; _tradingEngine.CancelPendingOrder(order.Id, reason, request.Comment); var result = new BackendResponse <bool> { Result = true }; _consoleWriter.WriteLine( $"action order.cancel for clientId = {request.ClientId}, orderId = {request.OrderId}"); _operationsLogService.AddLog("action order.cancel", request.ClientId, order.AccountId, request.ToJson(), result.ToJson()); return(result); }
public MtBackendResponse <bool> CancelOrder([FromBody] CloseOrderBackendRequest request) { if (!_ordersCache.WaitingForExecutionOrders.TryGetOrderById(request.OrderId, out var order)) { return(new MtBackendResponse <bool> { Message = "Order not found" }); } if (_assetDayOffService.IsDayOff(order.Instrument)) { return(new MtBackendResponse <bool> { Message = "Trades for instrument are not available" }); } if (order.ClientId != request.ClientId || order.AccountId != request.AccountId) { return(new MtBackendResponse <bool> { Message = "Order is not available for user" }); } _tradingEngine.CancelPendingOrder(order.Id, OrderCloseReason.Canceled); var result = new MtBackendResponse <bool> { Result = true }; _consoleWriter.WriteLine( $"action order.cancel for clientId = {request.ClientId}, orderId = {request.OrderId}"); _operationsLogService.AddLog("action order.cancel", request.ClientId, order.AccountId, request.ToJson(), result.ToJson()); return(result); }
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(ProductChangedEvent @event) { switch (@event.ChangeType) { case ChangeType.Creation: case ChangeType.Edition: if ([email protected]) { _log.WriteInfo(nameof(ProductChangedProjection), nameof(Handle), $"ProductChangedEvent received for productId: {@event.NewValue.ProductId}, but it was ignored because it has not been started yet."); return; } break; case ChangeType.Deletion: if ([email protected]) { _log.WriteInfo(nameof(ProductChangedProjection), nameof(Handle), $"ProductChangedEvent received for productId: {@event.OldValue.ProductId}, but it was ignored because it has not been started yet."); return; } break; default: throw new ArgumentOutOfRangeException(); } if (@event.ChangeType == ChangeType.Deletion) { CloseAllOrders(); ValidatePositions(@event.OldValue.ProductId); _assetPairsCache.Remove(@event.OldValue.ProductId); } else { if (@event.NewValue.IsDiscontinued) { CloseAllOrders(); RemoveQuoteFromCache(); } await _tradingInstrumentsManager.UpdateTradingInstrumentsCacheAsync(); var isAdded = _assetPairsCache.AddOrUpdate(AssetPair.CreateFromProduct(@event.NewValue, _mtSettings.DefaultLegalEntitySettings.DefaultLegalEntity)); if (@event.NewValue.TradingCurrency != AssetPairConstants.BaseCurrencyId) { _assetPairsCache.AddOrUpdate(AssetPair.CreateFromCurrency(@event.NewValue.TradingCurrency, _mtSettings.DefaultLegalEntitySettings.DefaultLegalEntity)); } //only for product if (isAdded) { await _scheduleSettingsCacheService.UpdateScheduleSettingsAsync(); } if (@event.ChangeType == ChangeType.Edition && @event.OldValue.IsTradingDisabled != @event.NewValue.IsTradingDisabled) { await HandleTradingDisabled(@event.NewValue, @event.Username); } } void RemoveQuoteFromCache() { var result = _quoteCache.RemoveQuote(@event.OldValue.ProductId); if (result != RemoveQuoteErrorCode.None) { _log.WriteWarning(nameof(ProductChangedProjection), nameof(RemoveQuoteFromCache), result.Message); } } void CloseAllOrders() { try { foreach (var order in _orderReader.GetPending() .Where(x => x.AssetPairId == @event.OldValue.ProductId)) { _tradingEngine.CancelPendingOrder(order.Id, null, null, OrderCancellationReason.InstrumentInvalidated); } } catch (Exception exception) { _log.WriteError(nameof(ProductChangedProjection), nameof(CloseAllOrders), exception); throw; } } void ValidatePositions(string assetPairId) { var positions = _orderReader.GetPositions(assetPairId); if (positions.Any()) { _log.WriteFatalError(nameof(ProductChangedProjection), nameof(ValidatePositions), new Exception( $"{positions.Length} positions are opened for [{assetPairId}], first: [{positions.First().Id}].")); } } }
public async Task Handle(AssetPairChangedEvent @event) { //deduplication is not required, it's ok if an object is updated multiple times if (@event.AssetPair?.Id == null) { await _log.WriteWarningAsync(nameof(AssetPairProjection), nameof(Handle), "AssetPairChangedEvent contained no asset pair id"); return; } if (IsDelete(@event)) { CloseAllOrders(); ValidatePositions(@event.AssetPair.Id); _assetPairsCache.Remove(@event.AssetPair.Id); } else { if (@event.AssetPair.IsDiscontinued) { CloseAllOrders(); } _assetPairsCache.AddOrUpdate(new AssetPair( id: @event.AssetPair.Id, name: @event.AssetPair.Name, baseAssetId: @event.AssetPair.BaseAssetId, quoteAssetId: @event.AssetPair.QuoteAssetId, accuracy: @event.AssetPair.Accuracy, legalEntity: @event.AssetPair.LegalEntity, basePairId: @event.AssetPair.BasePairId, matchingEngineMode: @event.AssetPair.MatchingEngineMode.ToType <MatchingEngineMode>(), stpMultiplierMarkupBid: @event.AssetPair.StpMultiplierMarkupBid, stpMultiplierMarkupAsk: @event.AssetPair.StpMultiplierMarkupAsk, isSuspended: @event.AssetPair.IsSuspended, isFrozen: @event.AssetPair.IsFrozen, isDiscontinued: @event.AssetPair.IsDiscontinued )); } await _scheduleSettingsCacheService.UpdateSettingsAsync(); void CloseAllOrders() { try { foreach (var order in _orderReader.GetPending().Where(x => x.AssetPairId == @event.AssetPair.Id)) { _tradingEngine.CancelPendingOrder(order.Id, null, @event.OperationId, null, OrderCancellationReason.InstrumentInvalidated); } } catch (Exception exception) { _log.WriteError(nameof(AssetPairProjection), nameof(CloseAllOrders), exception); throw; } } void ValidatePositions(string assetPairId) { var positions = _orderReader.GetPositions(assetPairId); if (positions.Any()) { _log.WriteFatalError(nameof(AssetPairProjection), nameof(ValidatePositions), new Exception($"{positions.Length} positions are opened for [{assetPairId}], first: [{positions.First().Id}].")); } } }