From 488e4c3fd3aebcd63b9e05440656c69d499039fa Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:11:16 +0900 Subject: [PATCH] Sales Analyzer Page --- Core/DataObjects/PTMagicData.cs | 11 +- Core/DataObjects/ProfitTrailerData.cs | 163 +++++++----- Monitor/Pages/SalesAnalyzer.cshtml | 307 +++++++++++----------- Monitor/Pages/SalesAnalyzer.cshtml.cs | 106 +++++--- Monitor/Pages/SettingsGeneral.cshtml | 2 +- Monitor/Pages/_get/DashboardBottom.cshtml | 49 ++-- 6 files changed, 359 insertions(+), 279 deletions(-) diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index 7969a79..10f4d6b 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -475,7 +475,16 @@ namespace Core.Main.DataObjects.PTMagicData public int SoldTimes { get; set; } public double Avg { get; set; } } - + public class DailyStatsData + { + public string Date { get; set; } + public int TotalSales { get; set; } + public int TotalBuys { get; set; } + public double TotalProfit { get; set; } + public double AvgProfit { get; set; } + + public double AvgGrowth { get; set; } + } public class PTStrategy { public string type { get; set; } diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 212f8d2..5891e0c 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -22,6 +22,7 @@ namespace Core.Main.DataObjects private List _dailyTCV = new List(); private List _monthlyStats = new List(); private List _profitablePairs = new List(); + private List _dailyStats = new List(); private decimal? _totalProfit = null; public decimal? TotalProfit { @@ -49,7 +50,8 @@ namespace Core.Main.DataObjects private DateTime _dcaLogRefresh = DateTime.UtcNow; private DateTime _miscRefresh = DateTime.UtcNow; private DateTime _propertiesRefresh = DateTime.UtcNow; - private DateTime _profitablePairsRefresh = DateTime.UtcNow; + private DateTime _profitablePairsRefresh = DateTime.UtcNow; + private DateTime _dailyStatsRefresh = DateTime.UtcNow; private volatile object _dailyPNLLock = new object(); private volatile object _dailyTCVLock = new object(); private volatile object _monthlyStatsLock = new object(); @@ -59,8 +61,9 @@ namespace Core.Main.DataObjects private volatile object _dcaLock = new object(); private volatile object _miscLock = new object(); private volatile object _propertiesLock = new object(); - private volatile object _profitablePairsLock = new object(); - private TimeSpan? _offsetTimeSpan = null; + private volatile object _profitablePairsLock = new object(); + private volatile object _dailyStatsLock = new object(); + private TimeSpan? _offsetTimeSpan = null; public void DoLog(string message) { // Implement your logging logic here @@ -132,6 +135,74 @@ namespace Core.Main.DataObjects TimeZoneOffset = PTData.timeZoneOffset, }; } + public List DailyStats + { + get + { + if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) + { + lock (_dailyStatsLock) + { + if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) + { + using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats")) + using (var reader = new StreamReader(stream)) + using (var jsonReader = new JsonTextReader(reader)) + { + JObject basicSection = null; + JObject extraSection = null; + + while (jsonReader.Read()) + { + if (jsonReader.TokenType == JsonToken.PropertyName) + { + if ((string)jsonReader.Value == "basic") + { + jsonReader.Read(); // Move to the value of the "basic" property + basicSection = JObject.Load(jsonReader); + } + else if ((string)jsonReader.Value == "extra") + { + jsonReader.Read(); // Move to the value of the "extra" property + extraSection = JObject.Load(jsonReader); + } + } + + if (basicSection != null && extraSection != null) + { + break; + } + } + + if (basicSection != null) + { + if (extraSection != null) + { + JArray dailyStatsSection = (JArray)extraSection["dailyStats"]; + _dailyStats = dailyStatsSection.Select(j => BuildDailyStatsData(j as JObject)).ToList(); + _dailyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + } + } + } + return _dailyStats; + } + } + private DailyStatsData BuildDailyStatsData(dynamic dailyStatsDataJson) + { + return new DailyStatsData() + { + Date = dailyStatsDataJson["date"], + TotalSales = dailyStatsDataJson["totalSales"], + TotalBuys = dailyStatsDataJson["totalBuys"], + TotalProfit = dailyStatsDataJson["totalProfitCurrency"], + AvgProfit = dailyStatsDataJson["avgProfit"], + AvgGrowth = dailyStatsDataJson["avgGrowth"], + }; + } + public PropertiesData Properties { get @@ -198,7 +269,6 @@ namespace Core.Main.DataObjects return _stats; } } - private StatsData BuildStatsData(dynamic statsDataJson) { return new StatsData() @@ -295,10 +365,6 @@ namespace Core.Main.DataObjects return _dailyPNL; } } - public int GetTotalDays() - { - return DailyPNL?.Count ?? 0; - } private DailyPNLData BuildDailyPNLData(dynamic dailyPNLDataJson) { return new DailyPNLData() @@ -345,14 +411,8 @@ namespace Core.Main.DataObjects break; } } - if (basicSection != null) // && - //((_totalProfit == null || - //!Decimal.Equals(_totalProfit.Value, basicSection["totalProfit"].Value())) || - //(_totalSales == null || - //!Decimal.Equals(_totalSales.Value, basicSection["totalSales"].Value())))) - { - //_totalProfit = basicSection["totalProfit"].Value(); - //_totalSales = basicSection["totalSales"].Value(); + if (basicSection != null) + { if (extraSection != null) { JObject profitablePairsSection = (JObject)extraSection["profitablePairs"]; @@ -390,10 +450,6 @@ namespace Core.Main.DataObjects }; } - - - - public List DailyTCV { get @@ -408,47 +464,40 @@ namespace Core.Main.DataObjects using (var reader = new StreamReader(stream)) using (var jsonReader = new JsonTextReader(reader)) { - JObject basicSection = null; - JObject extraSection = null; + JObject basicSection = null; + JObject extraSection = null; while (jsonReader.Read()) { - if (jsonReader.TokenType == JsonToken.PropertyName) - { - if ((string)jsonReader.Value == "basic") - { - jsonReader.Read(); // Move to the value of the "basic" property - basicSection = JObject.Load(jsonReader); - } - else if ((string)jsonReader.Value == "extra") - { - jsonReader.Read(); // Move to the value of the "extra" property - extraSection = JObject.Load(jsonReader); - } - } + if (jsonReader.TokenType == JsonToken.PropertyName) + { + if ((string)jsonReader.Value == "basic") + { + jsonReader.Read(); // Move to the value of the "basic" property + basicSection = JObject.Load(jsonReader); + } + else if ((string)jsonReader.Value == "extra") + { + jsonReader.Read(); // Move to the value of the "extra" property + extraSection = JObject.Load(jsonReader); + } + } - if (basicSection != null && extraSection != null) - { - break; - } + if (basicSection != null && extraSection != null) + { + break; + } } - if (basicSection != null) // && - //((_totalProfit == null || - //!Decimal.Equals(_totalProfit.Value, basicSection["totalProfit"].Value())) || - //(_totalSales == null || - //!Decimal.Equals(_totalSales.Value, basicSection["totalSales"].Value())))) - { - //_totalProfit = basicSection["totalProfit"].Value(); - //_totalSales = basicSection["totalSales"].Value(); - + if (basicSection != null) + { if (extraSection != null) { JArray dailyTCVSection = (JArray)extraSection["dailyTCVStats"]; _dailyTCV = dailyTCVSection.Select(j => BuildDailyTCVData(j as JObject)).ToList(); _dailyTCVRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); } - } + } } } } @@ -509,14 +558,7 @@ namespace Core.Main.DataObjects } if (basicSection != null)// && - //((_totalProfit == null || - //!Decimal.Equals(_totalProfit.Value, basicSection["totalProfit"].Value())) || - //(_totalSales == null || - //!Decimal.Equals(_totalSales.Value, basicSection["totalSales"].Value())))) - { - //_totalProfit = basicSection["totalProfit"].Value(); - //_totalSales = basicSection["totalSales"].Value(); - + { if (extraSection != null) { JArray monthlyStatsSection = (JArray)extraSection["monthlyStats"]; @@ -532,11 +574,6 @@ namespace Core.Main.DataObjects } } - public int GetTotalMonths() - { - return MonthlyStats?.Count ?? 0; - } - private MonthlyStatsData BuildMonthlyStatsData(dynamic monthlyStatsDataJson) { return new MonthlyStatsData() @@ -580,7 +617,7 @@ namespace Core.Main.DataObjects this.BuildSellLogData(sellDataPage); pageIndex++; requestedPages++; -Console.WriteLine($"Importing sale: {pageIndex}"); +Console.WriteLine($"Importing salesLog: {pageIndex}"); } else diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index cc42f5c..43edd36 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -1,5 +1,6 @@ @page @model SalesAnalyzer +@using System.Globalization @{ ViewData["Title"] = ""; } @@ -59,21 +60,39 @@ -@if (Model.PTData.SellLog.Count == 0) { +
-
-
-

No Sales!

-

Sorry, but your Profit Trailer did not sell anything so far. Please wait for the bot to have at least one sale and you will start seeing data in here.

+
+
+ @{ + var days = Math.Min(Model.DailyStats.Count, 30); + } +

Daily Buys/Sales (@days Days) +
+ +
+

+
+ +
+
+

Daily Profit (All Time) + @if (!Model.ProfitChartDataJSON.Equals("")) { +
+ +
+ } else { +

Unable to load graph, no sales data found.

+ }

-} else { +
@{ - int totalDays = Model.PTData.GetTotalDays(); + int totalDays = Model.PTData.DailyPNL.Count; double startBalance = Model.MiscData.StartBalance; double totalSales = Model.PTData.Stats.TotalSales; double totalProfit = Model.PTData.Stats.TotalProfit; @@ -92,14 +111,14 @@ double avgMonthlyGain = totalPercentGain / totalMonths; double avgMonthlyFunding = totalFundingFees / totalMonths; } -

Sales Analysis

     (@startDate - @endDate) +

Averages

     (@startDate - @endDate) - - + @@ -111,7 +130,7 @@ - + @@ -119,14 +138,14 @@ @if(Model.PropertiesData.IsLeverageExchange) { - + } - + @@ -200,30 +219,87 @@
-
-

Daily Sales -
- -
-

-
- -
-
-

Daily Profit - @if (!Model.ProfitChartDataJSON.Equals("")) { -
- -
- } else { -

Unable to load graph, no sales data found.

+
+ @{ + var maxDays = Math.Min(Model.DailyStats.Count, Math.Min(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries, 30)); } +

Last @maxDays Days

+

TotalAVG/Day (@totalDays Days)AVG/Month (@totalMonths Months) AVG/DayAVG/Month (@totalMonths Months)
@avgMonthlySales
Profit @Model.PTData.Misc.Market @Model.PTData.Misc.Market @totalProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @Math.Round(totalProfit / totalDays, 8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @avgMonthlyProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))
Funding FeesFunding @Html.Raw(Math.Round(totalFundingFees,8).ToString("#0.00000000", new System.Globalization.CultureInfo("en-US"))) @Html.Raw(Math.Round(totalFundingFees / totalDays,8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))) @Html.Raw(Math.Round(totalFundingFees / totalMonths,8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")))
Profit @Model.PTData.Properties.Currency@Model.PTData.Properties.Currency @Html.Raw(Math.Round(totalProfitFiat,0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))) @Html.Raw(Math.Round(totalProfitFiat / totalDays, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))) @Html.Raw(Math.Round(totalProfitFiat / totalMonths, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))
+ + + + + + + + + + + + @{ + for (int i = 0; i < maxDays; i++) + { + DateTime salesDate = DateTime.ParseExact(Model.PTData.DailyStats[i].Date, "d-M-yyyy", CultureInfo.InvariantCulture); + var buys = Model.PTData.DailyStats[i].TotalBuys; + var sales = Model.PTData.DailyStats[i].TotalSales; + var profit = Model.PTData.DailyStats[i].TotalProfit; + var avgProfit = Model.PTData.DailyStats[i].AvgProfit; + var profitFiat = Math.Round(profit * Model.PTData.Misc.FiatConversionRate, 0); + + + + + + + + + + } + } + +
DayBuysSalesProfit @Model.Summary.MainMarketProfit @Model.PTData.Properties.Currency% Gain
@salesDate.ToString("yyyy-MM-dd")@buys@sales@profit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Math.Round(profitFiat,0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))@avgProfit.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
-
-
-
+
+
+ @{ + var maxMonths = Math.Min(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries, Model.MonthlyStats.Count); + } +

Last @maxMonths months

+ + + + + + + + + + + + @{ + for (int i = 0; i < maxMonths; i++) + { + DateTime monthDate = DateTime.ParseExact(Model.PTData.MonthlyStats[i].Month, "M-yyyy", CultureInfo.InvariantCulture); + string monthName = monthDate.ToString("MMMM yyyy", CultureInfo.InvariantCulture); + var sales = Model.PTData.MonthlyStats[i].TotalSales; + var profit = Model.PTData.MonthlyStats[i].TotalProfitCurrency; + var profitFiat = Math.Round(profit * Model.PTData.Misc.FiatConversionRate, 0); + var growth = Model.PTData.MonthlyStats[i].AvgGrowth; + + + + + + + + } + } + +
MonthSalesProfit @Model.Summary.MainMarketProfit @Model.PTData.Properties.Currency% Growth
@monthName@sales@profit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@profitFiat@growth %
+
+

Top @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets Sales Markets

@@ -262,109 +338,8 @@
-
-
-
-
-

Last @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries days

- - - - - - - - - - - - @for (DateTime salesDate = Model.DateTimeNow.DateTime.Date; salesDate >= Model.DateTimeNow.DateTime.AddDays(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries) && salesDate >= Model.MinSellLogDate; salesDate = salesDate.AddDays(-1)) { - List salesDateSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate); - double salesDateProfit = 0; - salesDateProfit = salesDateSales.Sum(sl => sl.Profit); - double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2); - double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesDate); - double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2); - - - - - - - - } - -
DaySalesProfit @Model.Summary.MainMarketProfit @Model.Summary.MainFiatCurrency% Gain
@salesDate.ToShortDateString()@salesDateSales.Count@salesDateProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Model.MainFiatCurrencySymbol + salesDateProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@salesDateGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
-
-
- -
-
-

Last @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries months

- - - - - - - - - - - - - - @{ - DateTime minSellLogMonthDate = new DateTime(Model.MinSellLogDate.Year, Model.MinSellLogDate.Month, 1).Date; - DateTime salesMonthStartDate = new DateTime(Model.DateTimeNow.DateTime.Year, Model.DateTimeNow.DateTime.Month, 1).Date; - } - @for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= Model.DateTimeNow.DateTime.AddMonths(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries) && salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) { - List salesMonthSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year); - double salesDateProfit = 0; - salesDateProfit = salesMonthSales.Sum(sl => sl.Profit); - double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2); - double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesMonthDate); - double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2); - double salesDateAVGDailyGain = 0; - double monthDailyProfit = 0; - int days = 0; - for (int d = 1; d <= DateTime.DaysInMonth(salesMonthDate.Year, salesMonthDate.Month); d++) { - DateTime monthDay = salesMonthDate.AddDays(-salesMonthDate.Day + d); - if (monthDay <= Model.DateTimeNow) { - days++; - List monthDaySales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == monthDay.Date); - double monthDayProfit = 0; - monthDayProfit = monthDaySales.Sum(sl => sl.Profit); - double monthDayStartBalance = Model.PTData.GetSnapshotBalance(monthDay); - monthDailyProfit += Math.Round(monthDayProfit / monthDayStartBalance * 100, 2); - } - } - salesDateAVGDailyGain = Math.Round(monthDailyProfit / days, 2); - - - - - - - - - } - -
MonthSalesProfit @Model.Summary.MainMarketProfit @Model.Summary.MainFiatCurrency% GainAVG %/Day
@salesMonthDate.ToString("MMMM", new System.Globalization.CultureInfo("en-US"))@salesMonthSales.Count@salesDateProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Model.MainFiatCurrencySymbol + salesDateProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@salesDateGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@salesDateAVGDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
-
-
-
- - - - @* *@ +
} @section Scripts { @@ -387,32 +362,54 @@ + +