public ProfitInfo GetTotalReport(ProfitInfo[] profits) { var total = new ProfitInfo(); foreach (var profit in profits) { total.TradesCount += profit.TradesCount; total.BuysCount += profit.BuysCount; total.SellsCount += profit.SellsCount; total.TotalBought += profit.TotalBought; total.TotalSold += profit.TotalSold; total.TotalBoughtQuote += profit.TotalBoughtQuote; total.TotalSoldQuote += profit.TotalSoldQuote; total.Pnl += profit.Pnl; total.PnlNoExcess += profit.PnlNoExcess; total.PnlWithFee += profit.PnlWithFee; total.DisplayWithFee = profit.DisplayWithFee; total.QuoteSymbol = profit.QuoteSymbol; total.BaseSymbol = profit.BaseSymbol; total.MaxInventory = profit.MaxInventory; total.MaxInventoryLimit = profit.MaxInventoryLimit; total.OrderSize = profit.OrderSize; } total.AverageBuyPrice = total.TotalBoughtQuote / total.TotalBought; total.AverageSellPrice = total.TotalSoldQuote / total.TotalSold; var formattedTotal = $"{total} with excess"; total.Report = formattedTotal; total.Day = null; total.Month = null; total.Year = null; return(total); }
public ProfitInfo[] GetReportPerDays(int year, int month) { var grouped = _trades .Where(x => x.TimestampDate.Month == month) .GroupBy(x => x.TimestampDate.Day); var reports = new List <ProfitInfo>(); ProfitInfo total = null; foreach (var group in grouped) { var trades = group.ToArray(); var dayReport = GetReport(trades.ToArray()); var formatted = $"day: {group.Key:00}, {dayReport}"; dayReport.Report = formatted; dayReport.Day = group.Key; dayReport.Year = year; dayReport.Month = month; reports.Add(dayReport); if (total == null) { total = dayReport.Clone(); total.AverageBuy = 0; total.AverageSell = 0; } else { total.TradesCount += dayReport.TradesCount; total.BuysCount += dayReport.BuysCount; total.SellsCount += dayReport.SellsCount; total.TotalBought += dayReport.TotalBought; total.TotalSold += dayReport.TotalSold; total.Pnl += dayReport.Pnl; total.PnlNoExcess += dayReport.PnlNoExcess; total.PnlWithFee += dayReport.PnlWithFee; } } if (total != null) { var formattedTotal = $"total: __, {total}"; total.Report = formattedTotal; total.Day = null; reports.Add(total); } return(reports.ToArray()); }
private static void Visualize(BacktestConfig backtest, ProfitComputer computer, IStrategy strategy, int maxInv, ProfitInfo report, ProfitInfo[] days, ProfitInfo[] months) { if (computer == null || backtest.Visualize == null || !backtest.Visualize.Value) { return; } PrepareWebVisualization(backtest, computer, strategy, maxInv, report, report, days, months); var chart = new ChartVisualizer(); var filename = Path.GetFileName(backtest.DirectoryPath); var strategyName = strategy.GetType().Name.ToLower(); var pnl = report.Pnl; var name = $"{pnl:#.00} {backtest.QuoteSymbol} (max inv: {maxInv}) "; var nameWithFee = $"{report.PnlWithFee:#.00} {backtest.QuoteSymbol} (max inv: {maxInv}) "; var dir = GetPathToReportDir(backtest); var pattern = ExtractFromPattern(backtest); var targetFile = Path.Combine(dir, $"{pattern}__{strategyName}__{maxInv}__{pnl:0}"); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } var bars = computer.Bars; var totalBars = bars.Length; if (backtest.VisualizeSkipBars.HasValue) { bars = bars.Skip(backtest.VisualizeSkipBars.Value).ToArray(); } if (backtest.VisualizeLimitBars.HasValue) { bars = bars.Take(backtest.VisualizeLimitBars.Value).ToArray(); } var minIndex = bars.Min(x => x.Index); var maxIndex = bars.Max(x => x.Index); var trades = computer.Trades .Where(x => x.BarIndex >= minIndex && x.BarIndex <= maxIndex) .ToArray(); chart.Plot(name, nameWithFee, targetFile, totalBars, bars, trades, days, months); }
public ProfitInfo[] GetReportByMonth() { var grouped = _trades.GroupBy(x => new { x.TimestampDate.Year, x.TimestampDate.Month }); var reports = new List <ProfitInfo>(); ProfitInfo total = null; foreach (var group in grouped) { var trades = group.ToArray(); var monthReport = GetReport(trades.ToArray()); var formatted = $"month: {group.Key.Month:00}/{group.Key.Year}, {monthReport}"; monthReport.Report = formatted; monthReport.Year = group.Key.Year; monthReport.Month = group.Key.Month; reports.Add(monthReport); if (total == null) { total = monthReport.Clone(); total.AverageBuy = 0; total.AverageSell = 0; } else { total.TradesCount += monthReport.TradesCount; total.BuysCount += monthReport.BuysCount; total.SellsCount += monthReport.SellsCount; total.TotalBought += monthReport.TotalBought; total.TotalSold += monthReport.TotalSold; total.Pnl += monthReport.Pnl; total.PnlNoExcess += monthReport.PnlNoExcess; total.PnlWithFee += monthReport.PnlWithFee; } } if (total != null) { var formattedTotal = $"total: __, {total}"; total.Report = formattedTotal; total.Month = null; reports.Add(total); } return(reports.ToArray()); }
public static ProfitInfo ComputeProfitComplex(TradeModel[] executedOrders, double fee) { double diff = 0; var bids = executedOrders.Where(x => x.Amount > 0).OrderBy(x => x.Timestamp).ToArray(); var asks = executedOrders.Where(x => x.Amount < 0).OrderBy(x => x.Timestamp).ToArray(); var totalBidAmount = bids.Sum(x => Math.Abs(x.Amount)); var totalAskAmount = asks.Sum(x => Math.Abs(x.Amount)); var excessAmount = totalBidAmount - totalAskAmount; var totalBidQuote = bids.Sum(x => Math.Abs(x.Amount * x.Price)); var totalAskQuote = asks.Sum(x => Math.Abs(x.Amount * x.Price)); var result = new ProfitInfo() { Pnl = 0, TradesCount = executedOrders.Length, BuysCount = bids.Length, SellsCount = asks.Length, TotalBought = totalBidAmount, TotalSold = totalAskAmount, TotalBoughtQuote = totalBidQuote, TotalSoldQuote = totalAskQuote, AverageBuyPrice = ComputeAveragePrice(bids), AverageSellPrice = ComputeAveragePrice(asks), WinRate = ComputeWinRate(executedOrders), ExcessAmount = excessAmount }; if (!bids.Any() || !asks.Any()) { return(result); } var amountForBid = totalBidAmount; foreach (var bidTrade in bids) { var amount = bidTrade.Amount; var price = bidTrade.Price; amountForBid -= amount; if (amountForBid >= 0) { diff -= ComputeOrderValue(amount, price, fee, true); } else { var amountTmp = amount + amountForBid; diff -= ComputeOrderValue(amountTmp, price, fee, true); break; } } var amountForSell = totalAskAmount; foreach (var askTrade in asks) { var amount = Math.Abs(askTrade.Amount); var price = askTrade.Price; amountForSell -= amount; if (amountForSell >= 0) { diff += ComputeOrderValue(amount, price, fee, false); } else { var amountTmp = amount + amountForSell; diff += ComputeOrderValue(amountTmp, price, fee, false); break; } } double lastTrade; if (excessAmount > 0) { var lastPrice = bids.LastOrDefault()?.Price ?? 0; lastTrade = excessAmount * lastPrice; } else { var lastPrice = asks.LastOrDefault()?.Price ?? 0; lastTrade = excessAmount * lastPrice; } var totalDiff = diff + lastTrade; result.PnlNoExcess = ComputeProfitNoExcess(executedOrders, fee); result.Pnl = totalDiff; return(result); }
public ProfitInfo[] GetReportPerDays(int year, int month, ref double maxTotalPnl, ref double minTotalPnl, ref double totalPnl) { var grouped = _trades .Where(x => x.TimestampDate.Year == year && x.TimestampDate.Month == month) .GroupBy(x => x.TimestampDate.Day) .ToArray(); var reports = new List <ProfitInfo>(); ProfitInfo total = null; for (int i = 0; i < grouped.Length; i++) { var group = grouped[i]; var last = group.Last(); var nextTrades = _trades .Where(x => x.TimestampDate > last.TimestampDate) .OrderBy(x => x.TimestampDate) .ToArray(); var trades = group.ToArray(); var dayReport = GetReport(trades.ToArray(), nextTrades); if (dayReport == ProfitInfo.Empty) { continue; } if (total == null) { total = dayReport.Clone(); total.Pnl += totalPnl; total.AverageBuyPrice = 0; total.AverageSellPrice = 0; } else { total.TradesCount += dayReport.TradesCount; total.BuysCount += dayReport.BuysCount; total.SellsCount += dayReport.SellsCount; total.TotalBought += dayReport.TotalBought; total.TotalSold += dayReport.TotalSold; total.TotalBoughtQuote += dayReport.TotalBoughtQuote; total.TotalSoldQuote += dayReport.TotalSoldQuote; total.Pnl += dayReport.Pnl; total.PnlNoExcess += dayReport.PnlNoExcess; total.PnlWithFee += dayReport.PnlWithFee; } var previousMaxPnl = maxTotalPnl; maxTotalPnl = Math.Max(maxTotalPnl, total.Pnl); if (maxTotalPnl > previousMaxPnl) { minTotalPnl = maxTotalPnl; } minTotalPnl = Math.Min(minTotalPnl, total.Pnl); var drawdown = (minTotalPnl - maxTotalPnl) / maxTotalPnl; if (drawdown < 0) { dayReport.MaxDrawdownPercentage = drawdown; } if (totalPnl <= 0) { totalPnl = total.Pnl; } else { dayReport.ProfitPercentage = (total.Pnl - totalPnl) / totalPnl; totalPnl = total.Pnl; } var formatted = $"day: {group.Key:00}, {dayReport}"; dayReport.Report = formatted; dayReport.Day = group.Key; dayReport.Year = year; dayReport.Month = month; reports.Add(dayReport); } //if (total != null) //{ // var formattedTotal = $"total: __, {total}"; // total.Report = formattedTotal; // total.Day = null; // reports.Add(total); //} return(reports.ToArray()); }