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