Exemple #1
0
        public Dictionary <string, (PositionCloseResult, Order)> StartLiquidation(string accountId,
                                                                                  OriginatorType originator, string additionalInfo, string operationId)
        {
            var result = new Dictionary <string, (PositionCloseResult, Order)>();

            var command = new StartLiquidationInternalCommand
            {
                OperationId     = operationId,
                CreationTime    = _dateService.Now(),
                AccountId       = accountId,
                LiquidationType = LiquidationType.Forced,
                OriginatorType  = originator,
                AdditionalInfo  = additionalInfo
            };

            _cqrsSender.SendCommandToSelf(command);

            var positions = _ordersCache.Positions.GetPositionsByAccountIds(accountId);

            var openPositions = new List <Position>();

            foreach (var position in positions)
            {
                switch (position.Status)
                {
                case PositionStatus.Active:
                    openPositions.Add(position);
                    break;

                case PositionStatus.Closing:
                    result.Add(position.Id, (PositionCloseResult.ClosingIsInProgress, null));
                    break;

                case PositionStatus.Closed:
                    result.Add(position.Id, (PositionCloseResult.Closed, null));
                    break;

                default:
                    throw new InvalidOperationException($"Position state {position.Status.ToString()} is not handled");
                }
            }

            foreach (var group in openPositions.GroupBy(p => p.AssetPairId))
            {
                // if asset pair is not available for trading, we will not try to close these positions
                if (_assetPairDayOffService.IsAssetTradingDisabled(group.Key))
                {
                    continue;
                }

                var positionGroup = group.ToArray();

                // if the net volume can be liquidated, we assume that positions will be closed without special liquidation
                if (CheckIfNetVolumeCanBeLiquidated(group.Key, positionGroup, out _))
                {
                    positionGroup.ForEach(p => result.Add(p.Id, (PositionCloseResult.Closed, null)));
                }
                else
                {
                    positionGroup.ForEach(p => result.Add(p.Id, (PositionCloseResult.ClosingStarted, null)));
                }
            }

            return(result);
        }
Exemple #2
0
        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>()
                });
            }
        }