Example #1
0
        public void Check_Order_InitialMargin()
        {
            _bestPriceConsumer.SendEvent(this,
                                         new BestPriceChangeEventArgs(
                                             new InstrumentBidAskPair {
                Instrument = "EURUSD", Ask = 1.3M, Bid = 1.2M
            }));

            _bestPriceConsumer.SendEvent(this,
                                         new BestPriceChangeEventArgs(
                                             new InstrumentBidAskPair {
                Instrument = "CHFJPY", Ask = 2.5M, Bid = 2.3M
            }));

            _fxRateCacheService.SetQuote(new InstrumentBidAskPair {
                Instrument = "EURUSD", Ask = 1.25M, Bid = 1.25M
            });
            _fxRateCacheService.SetQuote(new InstrumentBidAskPair {
                Instrument = "EURJPY", Ask = 2.3M, Bid = 2.3M
            });

            var order1 = TestObjectsFactory.CreateNewOrder(OrderType.Market, "EURUSD", Accounts[1],
                                                           MarginTradingTestsUtils.TradingConditionId, 1000);

            var order2 = TestObjectsFactory.CreateNewOrder(OrderType.Market, "CHFJPY", Accounts[1],
                                                           MarginTradingTestsUtils.TradingConditionId, -100);

            Assert.AreEqual(10.4M, _fplService.GetInitMarginForOrder(order1));
            Assert.AreEqual(10M, _fplService.GetInitMarginForOrder(order2));
        }
Example #2
0
        public void CheckIsEnoughBalance(Order order, IMatchingEngineBase matchingEngine)
        {
            var orderMargin            = _fplService.GetInitMarginForOrder(order);
            var accountMarginAvailable = _accountsCacheService.Get(order.AccountId).GetMarginAvailable();

            var quote = _quoteCacheService.GetQuote(order.AssetPairId);

            var openPrice         = order.Price ?? 0;
            var closePrice        = 0m;
            var directionForClose = order.Volume.GetClosePositionOrderDirection();

            if (quote.GetVolumeForOrderDirection(order.Direction) >= Math.Abs(order.Volume) &&
                quote.GetVolumeForOrderDirection(directionForClose) >= Math.Abs(order.Volume))
            {
                closePrice = quote.GetPriceForOrderDirection(directionForClose);

                if (openPrice == 0)
                {
                    openPrice = quote.GetPriceForOrderDirection(order.Direction);
                }
            }
            else
            {
                var openPriceInfo  = matchingEngine.GetBestPriceForOpen(order.AssetPairId, order.Volume);
                var closePriceInfo =
                    matchingEngine.GetPriceForClose(order.AssetPairId, order.Volume, openPriceInfo.externalProviderId);

                if (openPriceInfo.price == null || closePriceInfo == null)
                {
                    throw new ValidateOrderException(OrderRejectReason.NoLiquidity,
                                                     "Price for open/close can not be calculated");
                }

                closePrice = closePriceInfo.Value;

                if (openPrice == 0)
                {
                    openPrice = openPriceInfo.price.Value;
                }
            }

            var pnlInTradingCurrency = (closePrice - openPrice) * order.Volume;
            var fxRate = _cfdCalculatorService.GetQuoteRateForQuoteAsset(order.AccountAssetId,
                                                                         order.AssetPairId, order.LegalEntity,
                                                                         pnlInTradingCurrency > 0);
            var pnl = pnlInTradingCurrency * fxRate;

            // just in case... is should be always negative
            if (pnl > 0)
            {
                _log.WriteWarning(nameof(CheckIsEnoughBalance), order.ToJson(),
                                  $"Theoretical PnL at the moment of order execution is positive");
                pnl = 0;
            }

            if (accountMarginAvailable + pnl < orderMargin)
            {
                throw new ValidateOrderException(OrderRejectReason.NotEnoughBalance,
                                                 MtMessages.Validation_NotEnoughBalance,
                                                 $"Account available margin: {accountMarginAvailable}, order margin: {orderMargin}, pnl: {pnl} " +
                                                 $"(open price: {openPrice}, close price: {closePrice}, fx rate: {fxRate})");
            }
        }
Example #3
0
        public void CheckIsEnoughBalance(Order order, IMatchingEngineBase matchingEngine, decimal additionalMargin)
        {
            _log.WriteInfo(nameof(CheckIsEnoughBalance), new { Order = order, additionalMargin }.ToJson(),
                           "Start checking if account balance is enough ...");

            var orderMargin = _fplService.GetInitMarginForOrder(order);

            _log.WriteInfo(nameof(CheckIsEnoughBalance), new { Order = order, orderMargin }.ToJson(),
                           "Order margin calculated");

            var account = _accountsProvider.GetAccountById(order.AccountId);
            var accountMarginAvailable = account.GetMarginAvailable() + additionalMargin;

            _log.WriteInfo(nameof(CheckIsEnoughBalance), new { Order = order, Account = account, accountMarginAvailable }.ToJson(),
                           "Account margin available calculated");

            var quote = _quoteCacheService.GetQuote(order.AssetPairId);

            decimal openPrice;
            decimal closePrice;
            var     directionForClose = order.Volume.GetClosePositionOrderDirection();

            if (quote.GetVolumeForOrderDirection(order.Direction) >= Math.Abs(order.Volume) &&
                quote.GetVolumeForOrderDirection(directionForClose) >= Math.Abs(order.Volume))
            {
                closePrice = quote.GetPriceForOrderDirection(directionForClose);
                openPrice  = quote.GetPriceForOrderDirection(order.Direction);
            }
            else
            {
                var openPriceInfo  = matchingEngine.GetBestPriceForOpen(order.AssetPairId, order.Volume);
                var closePriceInfo =
                    matchingEngine.GetPriceForClose(order.AssetPairId, order.Volume, openPriceInfo.externalProviderId);

                if (openPriceInfo.price == null || closePriceInfo == null)
                {
                    throw new ValidateOrderException(OrderRejectReason.NoLiquidity,
                                                     "Price for open/close can not be calculated");
                }

                closePrice = closePriceInfo.Value;
                openPrice  = openPriceInfo.price.Value;
            }
            _log.WriteInfo(nameof(CheckIsEnoughBalance), new { Order = order, Quote = quote, openPrice, closePrice }.ToJson(),
                           "Open and close prices calculated");


            var pnlInTradingCurrency = (closePrice - openPrice) * order.Volume;
            var fxRate = _cfdCalculatorService.GetQuoteRateForQuoteAsset(order.AccountAssetId,
                                                                         order.AssetPairId, order.LegalEntity,
                                                                         pnlInTradingCurrency > 0);
            var pnl = pnlInTradingCurrency * fxRate;

            // just in case... is should be always negative
            if (pnl > 0)
            {
                _log.WriteWarning(nameof(CheckIsEnoughBalance), order.ToJson(),
                                  $"Theoretical PnL at the moment of order execution is positive");
                pnl = 0;
            }
            _log.WriteInfo(nameof(CheckIsEnoughBalance), new { Order = order, pnlInTradingCurrency, fxRate, pnl }.ToJson(),
                           "PNL calculated");

            var assetType = _assetPairsCache.GetAssetPairById(order.AssetPairId).AssetType;

            if (!_clientProfileSettingsCache.TryGetValue(account.TradingConditionId, assetType, out var clientProfileSettings))
            {
                throw new InvalidOperationException($"Client profile settings for [{account.TradingConditionId}] and asset type [{assetType}] were not found in cache");
            }

            var tradingInstrument =
                _tradingInstrumentsCache.GetTradingInstrument(account.TradingConditionId, order.AssetPairId);

            var entryCost = CostHelper.CalculateEntryCost(
                order.Price,
                order.Direction == OrderDirection.Buy ? Lykke.Snow.Common.Costs.OrderDirection.Buy : Lykke.Snow.Common.Costs.OrderDirection.Sell,
                quote.Ask,
                quote.Bid,
                fxRate,
                tradingInstrument.Spread,
                tradingInstrument.HedgeCost,
                _marginTradingSettings.BrokerDefaultCcVolume,
                _marginTradingSettings.BrokerDonationShare);

            _log.WriteInfo(nameof(CheckIsEnoughBalance),
                           new
            {
                OrderPrice = order.Price, OrderDirection = order.Direction, quote.Ask, quote.Bid, fxRate,
                tradingInstrument.Spread, tradingInstrument.HedgeCost, _marginTradingSettings.BrokerDefaultCcVolume,
                _marginTradingSettings.BrokerDonationShare, CalculatedEntryCost = entryCost
            }.ToJson(),
                           "Entry cost calculated");

            var exitCost = CostHelper.CalculateExitCost(
                order.Price,
                order.Direction == OrderDirection.Buy ? Lykke.Snow.Common.Costs.OrderDirection.Buy : Lykke.Snow.Common.Costs.OrderDirection.Sell,
                quote.Ask,
                quote.Bid,
                fxRate,
                tradingInstrument.Spread,
                tradingInstrument.HedgeCost,
                _marginTradingSettings.BrokerDefaultCcVolume,
                _marginTradingSettings.BrokerDonationShare);

            _log.WriteInfo(nameof(CheckIsEnoughBalance),
                           new
            {
                OrderPrice = order.Price, OrderDirection = order.Direction, quote.Ask, quote.Bid, fxRate,
                tradingInstrument.Spread, tradingInstrument.HedgeCost, _marginTradingSettings.BrokerDefaultCcVolume,
                _marginTradingSettings.BrokerDonationShare, CalculatedExitCost = exitCost
            }.ToJson(),
                           "Exit cost calculated");

            if (accountMarginAvailable + pnl - entryCost - exitCost < orderMargin)
            {
                throw new ValidateOrderException(OrderRejectReason.NotEnoughBalance,
                                                 MtMessages.Validation_NotEnoughBalance,
                                                 $"Account available margin: {accountMarginAvailable}, order margin: {orderMargin}, pnl: {pnl}, entry cost: {entryCost}, exit cost: {exitCost} " +
                                                 $"(open price: {openPrice}, close price: {closePrice}, fx rate: {fxRate})");
            }

            _log.WriteInfo(nameof(CheckIsEnoughBalance), new { Order = order, accountMarginAvailable, pnl, entryCost, exitCost, orderMargin }.ToJson(),
                           "Account balance is enough, validation succeeded.");
        }