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);
        }
示例#2
0
        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);
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }
示例#5
0
        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);
        }
示例#6
0
        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);
            }
        }
示例#7
0
        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}]."));
                }
            }
        }
示例#8
0
        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}]."));
                }
            }
        }