private async Task CheckApi(string key, ILiquidityEngineClient client)
        {
            if (_lastTime == default(DateTime))
            {
                _lastTime = DateTime.UtcNow.Subtract(Interval);
            }

            var fromDate = _lastTime;
            var toDate   = DateTime.UtcNow;

            Log.Info($"Check api Start. summary. Api: {key}. LastTime: {_lastTime:yyyy-MM-dd HH:mm:ss}");

            var sb = new StringBuilder();

            sb.AppendLine($"=== {fromDate:yyyy/MM/dd HH:mm:ss} ===");
            sb.AppendLine($"=== {toDate:yyyy/MM/dd HH:mm:ss} ===");
            sb.Append(Environment.NewLine);
            sb.AppendLine("Liquidity Engine Summary Statistics");
            sb.Append(Environment.NewLine);

            var countTrade = 0;

            try
            {
                var data = await client.Reports.GetPositionsReportAsync(fromDate, toDate, int.MaxValue);

                var report = data
                             .Where(x => x.IsClosed)
                             .Where(x => x.CloseDate > fromDate)
                             .Where(x => x.CloseDate <= toDate)
                             .ToList();

                var summary = await client.Reports.GetBalanceIndicatorsReportAsync();

                var markups = await client.InstrumentMarkupsApi.GetAllAsync();

                var grouped = report.GroupBy(x => x.AssetPairId).OrderBy(x => x.Key);

                var result        = new List <string>();
                var totalPl       = 0m;
                var totalTurnover = 0m;
                foreach (var assetPairTrades in grouped)
                {
                    var assetPairId = assetPairTrades.Key;
                    var count       = assetPairTrades.Count();
                    var buyVolume   = assetPairTrades.Where(x => x.Type == PositionType.Long).Sum(x => x.Volume);
                    var sellVolume  = assetPairTrades.Where(x => x.Type == PositionType.Short).Sum(x => x.Volume);
                    var isPnLInUsd  = assetPairTrades.All(x => x.PnLUsd.HasValue);
                    var pnLInUsd    = isPnLInUsd ? assetPairTrades.Sum(x => x.PnLUsd ?? 0) : (decimal?)null;
                    var pnL         = assetPairTrades.Sum(x => x.PnL ?? 0);

                    var assetPair = await _assetsServiceWithCache.TryGetAssetPairAsync(assetPairId);

                    var quoteAssetId = assetPair?.QuotingAssetId;
                    var asset        = await _assetsServiceWithCache.TryGetAssetAsync(quoteAssetId);

                    var quoteAssetDisplayId = quoteAssetId == null ? null : asset.Id;
                    var quoteAssetStr       = string.IsNullOrWhiteSpace(quoteAssetDisplayId) ? "[quote asset]" : quoteAssetDisplayId;

                    var pnLStr = pnLInUsd.HasValue ? $"{Math.Round(pnLInUsd.Value, 4)}$" : $"{Math.Round(pnL, 4)} {quoteAssetStr}";

                    var lastTrade    = assetPairTrades.Where(e => e.PnLUsd.HasValue && e.PnLUsd.Value > 0).OrderByDescending(e => e.ClosePrice).FirstOrDefault();
                    var latsUsdPrice = (lastTrade != null && lastTrade.PnLUsd.HasValue && lastTrade.PnL.HasValue)
                        ? lastTrade.PnLUsd.Value / lastTrade.PnL.Value
                        : 0m;
                    decimal turnover    = assetPairTrades.Sum(e => e.Volume * e.ClosePrice) ?? 0m;
                    var     turnoverUsd = turnover * latsUsdPrice;

                    var assetPairMessage = $"{assetPairId}; " +
                                           $"PL={pnLStr}; " +
                                           $"Turnover: {Math.Round(turnoverUsd, 0)} $; ";

                    result.Add(assetPairMessage);

                    assetPairMessage = $". Count: {count}; " +
                                       $"Sell: {Math.Round(sellVolume, 6)}; " +
                                       $"Buy: {Math.Round(buyVolume, 6)}; ";

                    var markup = markups.SingleOrDefault(x => x.AssetPairId == assetPairId);
                    if (markup != null)
                    {
                        var bidMarkup = $"{markup.TotalBidMarkup*100:0.##}%";
                        var askMarkup = markup.TotalAskMarkup == -1 ? "stopped" : $"{markup.TotalAskMarkup*100:0.##}%";

                        assetPairMessage += $"BidMarkup: {bidMarkup}, AskMarkup: {askMarkup}";
                    }

                    result.Add(assetPairMessage);

                    totalPl       += pnLInUsd ?? 0m;
                    totalTurnover += turnoverUsd;
                }

                sb.AppendLine(string.Join(Environment.NewLine, result));
                sb.Append($"");

                var fee = totalTurnover > 0 ? totalPl / totalTurnover : 0m;

                sb.Append($"Total PL: {Math.Round(totalPl, 2)} $, Turnover: {Math.Round(totalTurnover, 2)} $, fee: {Math.Round(fee * 100, 2)} %");

                _lastTime = DateTime.UtcNow;

                await SendMessage(sb.ToString());

                var summaryMessage = $"Equity: {(int) summary.Equity} $.  RiskExposure: {(int) summary.RiskExposure} $";
                await SendMessage(summaryMessage);

                var fiatSettlement = await client.Trades.GetSettlementTradesAsync();

                if (fiatSettlement.Any(e => !e.IsCompleted))
                {
                    sb.Clear();
                    sb.AppendLine("Detect fiat trades without settlement in lykke: ");
                    foreach (var model in fiatSettlement.Where(e => !e.IsCompleted))
                    {
                        sb.AppendLine($"{model.AssetPair}, {model.Type}, price: {model.Price}, volume: {model.Volume}");
                    }
                    await SendMessage(sb.ToString());
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }

            Log.Info($"Check api complete. Found: {countTrade} asset pairs. Api: {key}. LastTime: {_lastTime:yyyy-MM-dd HH:mm:ss}");
        }
        private async Task Execute(string key, ILiquidityEngineClient client)
        {
            Log.Info($"Started checking pnl stop loss engines triggered for LE with key '{key}'.");

            var sb = new StringBuilder();

            sb.AppendLine("Liquidity Engine PnL Stop Loss Engines Triggered");

            var newTriggered = new List <PnLStopLossEngineModel>();

            try
            {
                IReadOnlyList <PnLStopLossEngineModel> current = (await client.PnLStopLossEngines.GetAllAsync()).ToList();

                current = current.Where(x => x.Mode == PnLStopLossEngineMode.Active).ToList();

                lock (_sync)
                {
                    if (!_lastEnginesStates.ContainsKey(key))
                    {
                        newTriggered.AddRange(current);
                    }
                    else
                    {
                        var previous = _lastEnginesStates[key];

                        foreach (var engine in current)
                        {
                            if (previous.Any(x => x.Id == engine.Id))
                            {
                                continue;
                            }

                            newTriggered.Add(engine);
                        }
                    }

                    _lastEnginesStates[key] = current;
                }

                if (!newTriggered.Any())
                {
                    return;
                }

                foreach (var engine in newTriggered)
                {
                    var passedSinceStart = DateTime.UtcNow - engine.LastTime.Value;
                    var expectedTime     = DateTime.UtcNow + engine.Interval - passedSinceStart;
                    var remainingTime    = expectedTime - DateTime.UtcNow;

                    var markup = (engine.Markup * 100).ToString("0.##");

                    sb.AppendLine($"{engine.AssetPairId}: Threshold={engine.Threshold}$, Interval={engine.Interval}, Markup={markup}%. RemainingTime: {remainingTime:hh\\:mm\\:ss}.");
                }

                await SendMessage(sb.ToString());
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }

            Log.Info($"Finished checking pnl stop loss engines triggered for LE with key '{key}'.");
        }
        private async Task CheckApi(string key, ILiquidityEngineClient client)
        {
            var fromDate = DateTime.UtcNow.Date.AddDays(-1);
            var toDate   = DateTime.UtcNow.Date.AddDays(+1);

            if (!_lastClose.TryGetValue(key, out var lastClose))
            {
                lastClose       = DateTime.UtcNow;
                _lastClose[key] = lastClose;
            }

            Log.Info($"Check api started. Trades. Api: {key}. LastTime: {lastClose:yyyy-MM-dd HH:mm:ss}");

            var countTrade = 0;

            try
            {
                var data = await client.Reports.GetPositionsReportAsync(fromDate, toDate, 5000);

                var markups = await client.InstrumentMarkupsApi.GetAllAsync();

                var positions = data.Where(e => e.CloseDate > lastClose).ToList();

                foreach (var position in positions.OrderBy(e => e.CloseDate))
                {
                    var assetPair = await _assetsServiceWithCache.TryGetAssetPairAsync(position.AssetPairId);

                    var quoteAssetId = assetPair?.QuotingAssetId;
                    var asset        = await _assetsServiceWithCache.TryGetAssetAsync(quoteAssetId);

                    var quoteAssetDisplayId = quoteAssetId == null ? null : asset.Id;
                    var quoteAssetStr       = string.IsNullOrWhiteSpace(quoteAssetDisplayId) ? "[quote asset]" : quoteAssetDisplayId;

                    var markupModel = markups.Single(x => x.AssetPairId == position.AssetPairId);
                    var markupValue = position.Type == PositionType.Short ? markupModel.TotalAskMarkup : markupModel.TotalBidMarkup;
                    var markup      = markupValue == -1 ? "stopped" : (markupValue * 100).ToString("0.##") + "%";

                    var pnL        = position.PnL ?? 0;
                    var closePrice = position.ClosePrice ?? 0;
                    var pnLStr     = position.PnLUsd.HasValue ? $"{Math.Round(position.PnLUsd.Value, 4)}$" : $"{Math.Round(pnL, 4)} {quoteAssetStr}";

                    var message =
                        $"{position.AssetPairId}; " +
                        $"PL={pnLStr}; " +
                        $"{(position.Type == PositionType.Short ? "Sell" : "Buy")}; " +
                        $"Volume: {Math.Round(position.Volume, 6)}; " +
                        $"OpenPrice: {Math.Round(position.Price, 6)}; " +
                        $"ClosePrice: {Math.Round(closePrice, 6)}; " +
                        $"Close: {position.CloseDate:MM-dd HH:mm:ss}; " +
                        $"Markup: {markup}";

                    if (positions.Count >= 4800)
                    {
                        message += "; !!!max count of position in day, please add limit!!!";
                    }
                    await TelegramSender.SendTextMessageAsync(PublisherSettings.ChatId, message);

                    if (position.CloseDate.HasValue)
                    {
                        _lastClose[key] = position.CloseDate.Value;
                    }

                    countTrade++;
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                _lastClose[key] = DateTime.UtcNow;
            }

            Log.Info($"Check api completed. Found: {countTrade} trades. Api: {key}. LastTime: {lastClose:yyyy-MM-dd HH:mm:ss}");
        }