Example #1
0
        private List <MarginTradingAccount> UpdateClosePriceAndDetectStopout(InstrumentBidAskPair quote)
        {
            var positionsByAccounts = _ordersCache.Positions.GetPositionsByInstrument(quote.Instrument)
                                      .GroupBy(x => x.AccountId).ToDictionary(x => x.Key, x => x.ToArray());

            var accountsWithStopout = new List <MarginTradingAccount>();

            foreach (var accountPositions in positionsByAccounts)
            {
                var account         = _accountsCacheService.Get(accountPositions.Key);
                var oldAccountLevel = account.GetAccountLevel();

                foreach (var position in accountPositions.Value)
                {
                    var closeOrderDirection = position.Volume.GetClosePositionOrderDirection();
                    var closePrice          = quote.GetPriceForOrderDirection(closeOrderDirection);

                    if (quote.GetVolumeForOrderDirection(closeOrderDirection) < Math.Abs(position.Volume))
                    {
                        var defaultMatchingEngine = _meRouter.GetMatchingEngineForClose(position.OpenMatchingEngineId);

                        var orderbookPrice = defaultMatchingEngine.GetPriceForClose(position.AssetPairId, position.Volume,
                                                                                    position.ExternalProviderId);

                        if (orderbookPrice.HasValue)
                        {
                            closePrice = orderbookPrice.Value;
                        }
                    }

                    if (closePrice != 0)
                    {
                        position.UpdateClosePriceWithoutAccountUpdate(closePrice);

                        UpdateTrailingStops(position);
                    }
                }

                account.CacheNeedsToBeUpdated();

                var newAccountLevel = account.GetAccountLevel();

                if (newAccountLevel == AccountLevel.StopOut)
                {
                    accountsWithStopout.Add(account);
                }

                if (oldAccountLevel != newAccountLevel)
                {
                    _marginCallEventChannel.SendEvent(this, new MarginCallEventArgs(account, newAccountLevel));
                }
            }

            return(accountsWithStopout);
        }
Example #2
0
        public bool CheckIfNetVolumeCanBeLiquidated(string assetPairId, Position[] positions,
                                                    out string details)
        {
            var positionsWithDifferentAssetPair = positions.Where(p => p.AssetPairId != assetPairId).ToList();

            if (positionsWithDifferentAssetPair.Count > 0)
            {
                var positionsInfo = positionsWithDifferentAssetPair.Select(p => (p.Id, p.AssetPairId)).ToJson();

                throw new InvalidOperationException(
                          $"All positions should have the same asset pair {assetPairId}, but there are positions with different: {positionsInfo}");
            }

            foreach (var positionsGroup in positions.GroupBy(p => p.Direction))
            {
                var netPositionVolume = positionsGroup.Sum(p => p.Volume);

                //TODO: discuss and handle situation with different MEs for different positions
                //at the current moment all positions has the same asset pair
                //and every asset pair can be processed only by one ME
                var anyPosition = positionsGroup.First();
                var me          = _matchingEngineRouter.GetMatchingEngineForClose(anyPosition.OpenMatchingEngineId);
                //the same for externalProvider..
                var externalProvider = anyPosition.ExternalProviderId;

                if (me.GetPriceForClose(assetPairId, netPositionVolume, externalProvider) == null)
                {
                    details = $"Not enough depth of orderbook. Net volume : {netPositionVolume}.";
                    return(false);
                }
            }

            details = string.Empty;
            return(true);
        }
Example #3
0
        private bool CheckIfNetVolumeCanBeLiquidated(string accountId, string assetPairId, Position[] positions,
                                                     out string details)
        {
            var netPositionVolume = positions.Sum(p => p.Volume);

            var account           = _accountsCache.Get(accountId);
            var tradingInstrument = _tradingInstrumentsCacheService.GetTradingInstrument(account.TradingConditionId,
                                                                                         assetPairId);

            if (tradingInstrument.LiquidationThreshold > 0 &&
                Math.Abs(netPositionVolume) > tradingInstrument.LiquidationThreshold)
            {
                details = $"Threshold exceeded. Net volume : {netPositionVolume}. " +
                          $"Threshold : {tradingInstrument.LiquidationThreshold}.";
                return(false);
            }

            //TODO: discuss and handle situation with different MEs for different positions
            //at the current moment all positions has the same asset pair
            //and every asset pair can be processed only by one ME
            var anyPosition = positions.First();
            var me          = _matchingEngineRouter.GetMatchingEngineForClose(anyPosition.OpenMatchingEngineId);
            //the same for externalProvider..
            var externalProvider = anyPosition.ExternalProviderId;

            if (me.GetPriceForClose(assetPairId, netPositionVolume, externalProvider) == null)
            {
                details = $"Not enough depth of orderbook. Net volume : {netPositionVolume}.";
                return(false);
            }

            details = string.Empty;
            return(true);
        }
Example #4
0
        private void CheckIfWeCanOpenPosition(Order order, MatchedOrderCollection matchedOrders)
        {
            var accountAsset = _accountAssetsCacheService.GetAccountAsset(order.TradingConditionId, order.AccountAssetId, order.Instrument);

            _validateOrderService.ValidateInstrumentPositionVolume(accountAsset, order);

            order.MatchedOrders.AddRange(matchedOrders);
            order.OpenPrice = Math.Round(order.MatchedOrders.WeightedAveragePrice, order.AssetAccuracy);

            var defaultMatchingEngine = _meRouter.GetMatchingEngineForClose(order);

            var closePrice = defaultMatchingEngine.GetPriceForClose(order);

            if (!closePrice.HasValue)
            {
                throw new ValidateOrderException(OrderRejectReason.NoLiquidity, "No orders to match for close");
            }

            order.UpdateClosePrice(Math.Round(closePrice.Value, order.AssetAccuracy));

            //TODO: very strange check.. think about it one more time
            var guessAccount      = _accountUpdateService.GuessAccountWithNewActiveOrder(order);
            var guessAccountLevel = guessAccount.GetAccountLevel();

            if (guessAccountLevel != AccountLevel.None)
            {
                order.OpenPrice = 0;
                order.UpdateClosePrice(0);
                order.MatchedOrders = new MatchedOrderCollection();
            }

            if (guessAccountLevel == AccountLevel.MarginCall)
            {
                throw new ValidateOrderException(OrderRejectReason.AccountInvalidState,
                                                 "Opening the position will lead to account Margin Call level");
            }

            if (guessAccountLevel == AccountLevel.StopOUt)
            {
                throw new ValidateOrderException(OrderRejectReason.AccountInvalidState,
                                                 "Opening the position will lead to account Stop Out level");
            }
        }
Example #5
0
        private void OpenNewPosition(Order order, decimal volume)
        {
            if (order.ExecutionPrice == null)
            {
                _log.WriteWarning(nameof(OpenNewPosition), order.ToJson(),
                                  "Execution price is null. Position was not opened");
                return;
            }

            var position = new Position(order.Id, order.Code, order.AssetPairId, volume, order.AccountId,
                                        order.TradingConditionId, order.AccountAssetId, order.Price, order.MatchingEngineId,
                                        order.Executed.Value, order.Id, order.OrderType, order.Volume, order.ExecutionPrice.Value, order.FxRate,
                                        order.EquivalentAsset, order.EquivalentRate, order.RelatedOrders, order.LegalEntity, order.Originator,
                                        order.ExternalProviderId, order.FxAssetPairId, order.FxToAssetPairDirection, order.AdditionalInfo, order.ForceOpen);

            var defaultMatchingEngine = _meRouter.GetMatchingEngineForClose(position.OpenMatchingEngineId);

            var closePrice = defaultMatchingEngine.GetPriceForClose(position.AssetPairId, position.Volume,
                                                                    position.ExternalProviderId);

            position.UpdateClosePrice(closePrice ?? order.ExecutionPrice.Value);

            var isPositionAlreadyExist = _ordersCache.Positions.GetPositionsByInstrumentAndAccount(
                position.AssetPairId,
                position.AccountId).Any(p => p.Direction == position.Direction);

            _ordersCache.Positions.Add(position);

            var metadata = new PositionOpenMetadata {
                ExistingPositionIncreased = isPositionAlreadyExist
            };

            SendPositionHistoryEvent(position, PositionHistoryTypeContract.Open, 0,
                                     order.AdditionalInfo, metadata: metadata);

            ActivateRelatedOrders(position);
        }