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."); }