Beispiel #1
0
        private void LiquidatePositionsIfAnyAvailable(string operationId,
                                                      LiquidationOperationData data, ICommandSender sender)
        {
            var liquidationData = GetLiquidationData(data);

            if (!liquidationData.HasValue || !liquidationData.Value.Positions.Any())
            {
                sender.SendCommand(new FailLiquidationInternalCommand
                {
                    OperationId     = operationId,
                    CreationTime    = _dateService.Now(),
                    Reason          = "Nothing to liquidate",
                    LiquidationType = data.LiquidationType,
                }, _cqrsContextNamesSettings.TradingEngine);
            }
            else
            {
                sender.SendCommand(new LiquidatePositionsInternalCommand
                {
                    OperationId  = operationId,
                    CreationTime = _dateService.Now(),
                    PositionIds  = liquidationData.Value.Positions,
                    AssetPairId  = liquidationData.Value.AssetPairId,
                }, _cqrsContextNamesSettings.TradingEngine);
            }
        }
Beispiel #2
0
        private (string AssetPairId, string[] Positions)? GetLiquidationData(
            LiquidationOperationData data)
        {
            var positionsOnAccount = _ordersCache.Positions.GetPositionsByAccountIds(data.AccountId);

            //group positions and take only not processed, filtered and with open market
            var positionGroups = positionsOnAccount
                                 .Where(p => !data.ProcessedPositionIds.Contains(p.Id) &&
                                        (string.IsNullOrEmpty(data.AssetPairId) || p.AssetPairId == data.AssetPairId) &&
                                        (data.Direction == null || p.Direction == data.Direction))
                                 .GroupBy(p => p.AssetPairId)
                                 .Where(gr => !_assetPairDayOffService.IsDayOff(gr.Key))
                                 .ToArray();

            IGrouping <string, Position> targetPositions = null;

            //take positions from group with max margin used or max initially used margin
            targetPositions = positionGroups
                              .OrderByDescending(gr => gr.Sum(p => Math.Max(p.GetMarginMaintenance(), p.GetInitialMargin())))
                              .FirstOrDefault();

            if (targetPositions == null)
            {
                return(null);
            }

            return(targetPositions.Key, targetPositions.Select(p => p.Id).ToArray());
        }
Beispiel #3
0
        private void ContinueOrFinishLiquidation(string operationId, LiquidationOperationData data, ICommandSender sender)
        {
            void FinishWithReason(string reason) => sender.SendCommand(new FinishLiquidationInternalCommand
            {
                OperationId           = operationId,
                CreationTime          = _dateService.Now(),
                Reason                = reason,
                LiquidationType       = data.LiquidationType,
                ProcessedPositionIds  = data.ProcessedPositionIds,
                LiquidatedPositionIds = data.LiquidatedPositionIds,
            }, _cqrsContextNamesSettings.TradingEngine);

            var account = _accountsCacheService.TryGet(data.AccountId);

            if (account == null)
            {
                sender.SendCommand(new FailLiquidationInternalCommand
                {
                    OperationId     = operationId,
                    CreationTime    = _dateService.Now(),
                    Reason          = "Account does not exist",
                    LiquidationType = data.LiquidationType,
                }, _cqrsContextNamesSettings.TradingEngine);
                return;
            }

            var accountLevel = account.GetAccountLevel();

            if (data.LiquidationType == LiquidationType.Forced)
            {
                if (!_ordersCache.Positions.GetPositionsByAccountIds(data.AccountId)
                    .Any(x => (string.IsNullOrWhiteSpace(data.AssetPairId) || x.AssetPairId == data.AssetPairId) &&
                         x.OpenDate < data.StartedAt &&
                         (data.Direction == null || x.Direction == data.Direction)))
                {
                    FinishWithReason("All positions are closed");
                }
                else
                {
                    LiquidatePositionsIfAnyAvailable(operationId, data, sender);
                }

                return;
            }

            if (accountLevel < AccountLevel.StopOut)
            {
                FinishWithReason($"Account margin level is {accountLevel}");
            }
            else
            {
                LiquidatePositionsIfAnyAvailable(operationId, data, sender);
            }
        }
Beispiel #4
0
        private (string AssetPairId, string[] Positions)? GetLiquidationData(
            LiquidationOperationData data)
        {
            var positionsOnAccount = _ordersCache.Positions.GetPositionsByAccountIds(data.AccountId);

            if (data.LiquidationType == LiquidationType.Forced)
            {
                positionsOnAccount = positionsOnAccount.Where(p => p.OpenDate < data.StartedAt).ToList();

                //group positions by asset and pnl sign, take only not processed, filtered and with open market
                //groups are ordered by positive pnl first
                var targetPositionsByPnlSign = positionsOnAccount
                                               .Where(p => !data.ProcessedPositionIds.Contains(p.Id))
                                               .GroupBy(p => (p.AssetPairId, p.GetUnrealisedFpl() >= 0))
                                               .Where(gr => !_assetPairDayOffService.IsAssetTradingDisabled(gr.Key.AssetPairId))
                                               .OrderByDescending(gr => gr.Key.Item2)
                                               .FirstOrDefault();

                if (targetPositionsByPnlSign == null)
                {
                    return(null);
                }

                return(targetPositionsByPnlSign.Key.AssetPairId, targetPositionsByPnlSign.Select(p => p.Id).ToArray());
            }

            //group positions and take only not processed, filtered and with open market
            var positionGroups = positionsOnAccount
                                 .Where(p => !data.ProcessedPositionIds.Contains(p.Id) &&
                                        (string.IsNullOrEmpty(data.AssetPairId) || p.AssetPairId == data.AssetPairId) &&
                                        (data.Direction == null || p.Direction == data.Direction))
                                 .GroupBy(p => p.AssetPairId)
                                 .Where(gr => !_assetPairDayOffService.IsAssetTradingDisabled(gr.Key))
                                 .ToArray();


            IGrouping <string, Position> targetPositions = null;

            //take positions from group with max margin used or max initially used margin
            targetPositions = positionGroups
                              .OrderByDescending(gr => gr.Sum(p => Math.Max(p.GetMarginMaintenance(), p.GetInitialMargin())))
                              .FirstOrDefault();

            if (targetPositions == null)
            {
                return(null);
            }

            return(targetPositions.Key, targetPositions.Select(p => p.Id).ToArray());
        }