示例#1
0
        public async Task Handle(LiquidatePositionsInternalCommand command, IEventPublisher publisher)
        {
            var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>(
                operationName : LiquidationSaga.OperationName,
                id : command.OperationId);

            if (executionInfo?.Data == null)
            {
                return;
            }

            await _log.WriteInfoAsync(nameof(LiquidationCommandsHandler),
                                      nameof(LiquidatePositionsInternalCommand),
                                      command.ToJson(),
                                      "Checking if position liquidation should be failed");

            var account = _accountsCache.Get(executionInfo.Data.AccountId);

            if (ShouldFailExecution(account.GetAccountLevel(), executionInfo.Data.LiquidationType))
            {
                await _log.WriteWarningAsync(
                    nameof(LiquidationCommandsHandler),
                    nameof(LiquidatePositionsInternalCommand),
                    new { accountId = account.Id, accountLevel = account.GetAccountLevel().ToString() }.ToJson(),
                    $"Unable to liquidate positions since account level is not {ValidAccountLevel.ToString()}.");

                await _failureExecutor.ExecuteAsync(publisher,
                                                    account.Id,
                                                    command.OperationId,
                                                    $"Account level is not {ValidAccountLevel.ToString()}.");

                return;
            }

            var positions = _ordersCache.Positions
                            .GetPositionsByAccountIds(executionInfo.Data.AccountId)
                            .Where(p => command.PositionIds.Contains(p.Id))
                            .ToArray();

            if (!positions.Any())
            {
                publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent
                {
                    OperationId      = command.OperationId,
                    CreationTime     = _dateService.Now(),
                    LiquidationInfos = command.PositionIds.Select(p =>
                                                                  new LiquidationInfo
                    {
                        PositionId   = p,
                        IsLiquidated = false,
                        Comment      = "Opened position was not found"
                    }).ToList()
                });
                return;
            }

            if (!_liquidationHelper.CheckIfNetVolumeCanBeLiquidated(command.AssetPairId, positions, out var details))
            {
                publisher.PublishEvent(new NotEnoughLiquidityInternalEvent
                {
                    OperationId  = command.OperationId,
                    CreationTime = _dateService.Now(),
                    PositionIds  = command.PositionIds,
                    Details      = details
                });
                return;
            }

            var liquidationInfos = new List <LiquidationInfo>();

            var comment = string.Empty;

            switch (executionInfo.Data.LiquidationType)
            {
            case LiquidationType.Mco:
                comment = "MCO liquidation";
                break;

            case LiquidationType.Normal:
                comment = "Liquidation";
                break;

            case LiquidationType.Forced:
                comment = "Close positions group";
                break;
            }

            var positionGroups = positions
                                 .GroupBy(p => (p.AssetPairId, p.AccountId, p.Direction, p
                                                .OpenMatchingEngineId, p.ExternalProviderId, p.EquivalentAsset))
                                 .Select(gr => new PositionsCloseData(
                                             gr.FreezeOrder(),
                                             gr.Key.AccountId,
                                             gr.Key.AssetPairId,
                                             gr.Key.OpenMatchingEngineId,
                                             gr.Key.ExternalProviderId,
                                             executionInfo.Data.OriginatorType,
                                             executionInfo.Data.AdditionalInfo,
                                             gr.Key.EquivalentAsset,
                                             comment));

            foreach (var positionGroup in positionGroups)
            {
                try
                {
                    var(result, order) = await _tradingEngine.ClosePositionsAsync(positionGroup, false);

                    foreach (var position in positionGroup.Positions)
                    {
                        liquidationInfos.Add(new LiquidationInfo
                        {
                            PositionId   = position.Value.Id,
                            IsLiquidated = true,
                            Comment      = order != null ? $"Order: {order.Id}" : result.ToString()
                        });
                    }
                }
                catch (Exception ex)
                {
                    await _log.WriteWarningAsync(nameof(LiquidationCommandsHandler),
                                                 nameof(LiquidatePositionsInternalCommand),
                                                 $"Failed to close positions {string.Join(",", positionGroup.Positions.Select(p => p.Value.Id))} on liquidation operation #{command.OperationId}",
                                                 ex);

                    foreach (var position in positionGroup.Positions)
                    {
                        liquidationInfos.Add(new LiquidationInfo
                        {
                            PositionId   = position.Value.Id,
                            IsLiquidated = false,
                            Comment      = $"Close position failed: {ex.Message}"
                        });
                    }
                }
            }

            publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent
            {
                OperationId      = command.OperationId,
                CreationTime     = _dateService.Now(),
                LiquidationInfos = liquidationInfos
            });
        }
示例#2
0
        public async Task Handle(LiquidatePositionsInternalCommand command, IEventPublisher publisher)
        {
            var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>(
                operationName : LiquidationSaga.OperationName,
                id : command.OperationId);

            if (executionInfo?.Data == null)
            {
                return;
            }

            var positions = _ordersCache.Positions
                            .GetPositionsByAccountIds(executionInfo.Data.AccountId)
                            .Where(p => command.PositionIds.Contains(p.Id))
                            .ToArray();

            if (!positions.Any())
            {
                publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent
                {
                    OperationId      = command.OperationId,
                    CreationTime     = _dateService.Now(),
                    LiquidationInfos = command.PositionIds.Select(p =>
                                                                  new LiquidationInfo
                    {
                        PositionId   = p,
                        IsLiquidated = false,
                        Comment      = "Opened position was not found"
                    }).ToList()
                });
                return;
            }

            if (!CheckIfNetVolumeCanBeLiquidated(executionInfo.Data.AccountId, command.AssetPairId, positions,
                                                 out var details))
            {
                publisher.PublishEvent(new NotEnoughLiquidityInternalEvent
                {
                    OperationId  = command.OperationId,
                    CreationTime = _dateService.Now(),
                    PositionIds  = command.PositionIds,
                    Details      = details
                });
                return;
            }

            var liquidationInfos = new List <LiquidationInfo>();

            var comment = string.Empty;

            switch (executionInfo.Data.LiquidationType)
            {
            case LiquidationType.Mco:
                comment = "MCO liquidation";
                break;

            case LiquidationType.Normal:
                comment = "Liquidation";
                break;

            case LiquidationType.Forced:
                comment = "Close positions group";
                break;
            }

            var positionGroups = positions
                                 .GroupBy(p => (p.AssetPairId, p.AccountId, p.Direction, p
                                                .OpenMatchingEngineId, p.ExternalProviderId, p.EquivalentAsset))
                                 .Where(gr => gr.Any())
                                 .Select(gr => new PositionsCloseData(
                                             gr.ToList(),
                                             gr.Key.AccountId,
                                             gr.Key.AssetPairId,
                                             gr.Sum(x => x.Volume),
                                             gr.Key.OpenMatchingEngineId,
                                             gr.Key.ExternalProviderId,
                                             executionInfo.Data.OriginatorType,
                                             executionInfo.Data.AdditionalInfo,
                                             command.OperationId,
                                             gr.Key.EquivalentAsset,
                                             comment));

            foreach (var positionGroup in positionGroups)
            {
                try
                {
                    var order = await _tradingEngine.ClosePositionsAsync(positionGroup);

                    if (order.Status != OrderStatus.Executed && order.Status != OrderStatus.ExecutionStarted)
                    {
                        throw new Exception(order.RejectReasonText);
                    }

                    foreach (var position in positionGroup.Positions)
                    {
                        liquidationInfos.Add(new LiquidationInfo
                        {
                            PositionId   = position.Id,
                            IsLiquidated = true,
                            Comment      = $"Order: {order.Id}"
                        });
                    }
                }
                catch (Exception ex)
                {
                    await _log.WriteWarningAsync(nameof(LiquidationCommandsHandler),
                                                 nameof(LiquidatePositionsInternalCommand),
                                                 $"Failed to close positions {string.Join(",", positionGroup.Positions.Select(p => p.Id))} on liquidation operation #{command.OperationId}",
                                                 ex);

                    foreach (var position in positionGroup.Positions)
                    {
                        liquidationInfos.Add(new LiquidationInfo
                        {
                            PositionId   = position.Id,
                            IsLiquidated = false,
                            Comment      = $"Close position failed: {ex.Message}"
                        });
                    }
                }
            }

            publisher.PublishEvent(new PositionsLiquidationFinishedInternalEvent
            {
                OperationId      = command.OperationId,
                CreationTime     = _dateService.Now(),
                LiquidationInfos = liquidationInfos
            });
        }