From 6a364c95e72260959c3308c9ba07cb2048b234d4 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:54:28 +0900 Subject: [PATCH 01/14] 2.7 changes --- Core/DataObjects/PTMagicData.cs | 3 +- Core/DataObjects/ProfitTrailerData.cs | 11 +++- Core/Main/PTMagic.cs | 56 ++++++++--------- Core/ProfitTrailer/StrategyHelper.cs | 2 +- Monitor/Pages/SalesAnalyzer.cshtml | 2 +- Monitor/Pages/SettingsGeneral.cshtml | 21 ++++--- Monitor/Pages/SettingsGeneral.cshtml.cs | 5 +- Monitor/Pages/StatusSummary.cshtml | 61 +------------------ Monitor/wwwroot/assets/css/style.css | 18 +++--- PTMagic.sln | 37 +++++++++++ .../settings.general.json | 5 +- .../DevSettings/settings.general.json | 5 +- 12 files changed, 111 insertions(+), 115 deletions(-) create mode 100644 PTMagic.sln diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index 25865a1..fa6f69b 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -48,7 +48,7 @@ namespace Core.Main.DataObjects.PTMagicData public string TimezoneOffset { get; set; } = "+0:00"; public string MainFiatCurrency { get; set; } = "USD"; public string CoinMarketCapAPIKey { get; set; } - public string FreeCurrencyConverterAPIKey { get; set; } + //public string FreeCurrencyConverterAPIKey { get; set; } } public class Monitor @@ -64,6 +64,7 @@ namespace Core.Main.DataObjects.PTMagicData public int RefreshSeconds { get; set; } = 30; public int BagAnalyzerRefreshSeconds { get; set; } = 5; public int BuyAnalyzerRefreshSeconds { get; set; } = 5; + public int MaxSalesRecords { get; set; } = 99999; public int MaxTopMarkets { get; set; } = 20; public int MaxDailySummaries { get; set; } = 10; public int MaxMonthlySummaries { get; set; } = 10; diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index df01a9a..ca04707 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -113,14 +113,19 @@ namespace Core.Main.DataObjects bool exitLoop = false; int pageIndex = 1; - while (!exitLoop) + int maxPages = _systemConfiguration.GeneralSettings.Monitor.MaxSalesRecords; + int requestedPages = 0; + + while (!exitLoop && requestedPages < maxPages) { - var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?perPage=5000&sort=SOLDDATE&sortDirection=ASCENDING&page=" + pageIndex); + var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?Page=1&perPage=1&sort=SOLDDATE&sortDirection=DESCENDING&page=" + pageIndex); if (sellDataPage != null && sellDataPage.data.Count > 0) { // Add sales data page to collection this.BuildSellLogData(sellDataPage); pageIndex++; + requestedPages++; + } else { @@ -130,7 +135,7 @@ namespace Core.Main.DataObjects } // Update sell log refresh time - _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds -1); } } } diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index 82460d6..9c6e1b3 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -721,14 +721,14 @@ namespace Core.Main } // Check for CurrencyConverterApi Key - if (!this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey.Equals("")) - { - this.Log.DoLogInfo("FreeCurrencyConverterApi KEY found"); - } - else - { - this.Log.DoLogInfo("No FreeCurrencyConverterApi KEY specified. That's ok! But you can only use USD; apply for a key at: https://freecurrencyrates.com/en"); - } + //if (!this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey.Equals("")) + //{ + // this.Log.DoLogInfo("FreeCurrencyConverterApi KEY found"); + //} + //else + //{ + // this.Log.DoLogInfo("No FreeCurrencyConverterApi KEY specified. That's ok! But you can only use USD; apply for a key at: https://freecurrencyrates.com/en"); + //} } catch (System.NullReferenceException) { @@ -1132,28 +1132,28 @@ namespace Core.Main this.LastRuntimeSummary.MainFiatCurrency = this.LastMainFiatCurrency; this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = this.LastMainFiatCurrencyExchangeRate; - if (this.LastFiatCurrencyCheck < DateTime.UtcNow.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) - { - try - { - this.LastRuntimeSummary.MainFiatCurrency = this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency; - this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = BaseAnalyzer.GetMainFiatCurrencyRate(this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency, this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey, this.Log); - this.LastMainFiatCurrency = this.LastRuntimeSummary.MainFiatCurrency; - this.LastMainFiatCurrencyExchangeRate = this.LastRuntimeSummary.MainFiatCurrencyExchangeRate; + // if (this.LastFiatCurrencyCheck < DateTime.UtcNow.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) + // { + // try + // { + // this.LastRuntimeSummary.MainFiatCurrency = this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency; + // this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = BaseAnalyzer.GetMainFiatCurrencyRate(this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency, this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey, this.Log); + // this.LastMainFiatCurrency = this.LastRuntimeSummary.MainFiatCurrency; + // this.LastMainFiatCurrencyExchangeRate = this.LastRuntimeSummary.MainFiatCurrencyExchangeRate; - this.LastFiatCurrencyCheck = DateTime.UtcNow; - } - catch (Exception ex) - { + // this.LastFiatCurrencyCheck = DateTime.UtcNow; + // } + // catch (Exception ex) + // { - // Fallback to USD in case something went wrong - this.Log.DoLogError("Fixer.io exchange rate check error: " + ex.Message); - this.LastRuntimeSummary.MainFiatCurrency = "USD"; - this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = 1; - this.LastMainFiatCurrency = "USD"; - this.LastMainFiatCurrencyExchangeRate = 1; - } - } + // // Fallback to USD in case something went wrong + // this.Log.DoLogError("Fixer.io exchange rate check error: " + ex.Message); + // this.LastRuntimeSummary.MainFiatCurrency = "USD"; + // this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = 1; + // this.LastMainFiatCurrency = "USD"; + // this.LastMainFiatCurrencyExchangeRate = 1; + // } + // } } // Get current PT properties diff --git a/Core/ProfitTrailer/StrategyHelper.cs b/Core/ProfitTrailer/StrategyHelper.cs index cdd873b..1750c2e 100644 --- a/Core/ProfitTrailer/StrategyHelper.cs +++ b/Core/ProfitTrailer/StrategyHelper.cs @@ -437,7 +437,7 @@ namespace Core.ProfitTrailer } else { - strategyText += "FORM "; + strategyText += ""; } } else diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index f75c554..28240e1 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -25,7 +25,7 @@ totalCurrentValueString = Math.Round(Model.totalCurrentValue, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); } } - Total Account Value:   @totalCurrentValueString @Model.Summary.MainMarket + Total Current Value:   @totalCurrentValueString @Model.Summary.MainMarket Starting Value:   @Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance   @Model.Summary.MainMarket diff --git a/Monitor/Pages/SettingsGeneral.cshtml b/Monitor/Pages/SettingsGeneral.cshtml index 4989f93..82b7166 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml +++ b/Monitor/Pages/SettingsGeneral.cshtml @@ -124,12 +124,12 @@ -
+ @*
- +
-
+
*@
@@ -145,12 +145,12 @@
-
+ @*
-
+
*@ @@ -196,14 +196,14 @@
- +
- +
@@ -241,6 +241,13 @@
+ +
+ +
+
+ +
diff --git a/Monitor/Pages/SettingsGeneral.cshtml.cs b/Monitor/Pages/SettingsGeneral.cshtml.cs index d5f3c1e..39f6035 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml.cs +++ b/Monitor/Pages/SettingsGeneral.cshtml.cs @@ -74,11 +74,11 @@ namespace Monitor.Pages PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL = HttpContext.Request.Form["Application_ProfitTrailerMonitorURL"]; PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken = HttpContext.Request.Form["Application_ProfitTrailerServerAPIToken"]; PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset = HttpContext.Request.Form["Application_TimezoneOffset"]; - PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency = HttpContext.Request.Form["Application_MainFiatCurrency"]; + //PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency = HttpContext.Request.Form["Application_MainFiatCurrency"]; PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Application_FloodProtectionMinutes"], PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes); PTMagicConfiguration.GeneralSettings.Application.InstanceName = HttpContext.Request.Form["Application_InstanceName"]; PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey = HttpContext.Request.Form["Application_CoinMarketCapAPIKey"]; - PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"]; + //PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"]; PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = HttpContext.Request.Form["Monitor_IsPasswordProtected"].Equals("on"); PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart = HttpContext.Request.Form["Monitor_OpenBrowserOnStart"].Equals("on"); PTMagicConfiguration.GeneralSettings.Monitor.AnalyzerChart = HttpContext.Request.Form["Monitor_AnalyzerChart"]; @@ -89,6 +89,7 @@ namespace Monitor.Pages PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BagAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds); PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BuyAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds); PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform = HttpContext.Request.Form["Monitor_LinkPlatform"]; + PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxSalesRecords"], PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords); PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxTopMarkets"], PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets); PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxDailySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries); PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxMonthlySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries); diff --git a/Monitor/Pages/StatusSummary.cshtml b/Monitor/Pages/StatusSummary.cshtml index b3d31fc..46eb477 100644 --- a/Monitor/Pages/StatusSummary.cshtml +++ b/Monitor/Pages/StatusSummary.cshtml @@ -67,7 +67,7 @@
-

Active Settings

+

Active Settings  

@{ string maxCostCaption = "Initial"; } @@ -86,8 +86,6 @@ } - DCA Level - @Model.Summary.DCALevels.ToString(new System.Globalization.CultureInfo("en-US")) Min Vol. @Model.Summary.MinBuyVolume.ToString(new System.Globalization.CultureInfo("en-US")) @@ -101,62 +99,7 @@
-
-

Active Buy Strategies

- - - - @if (Model.Summary.BuyStrategies.Count == 0) { - - - - - - - } else { - char buyStrategyIndex = 'A'; - foreach (Core.Main.DataObjects.PTMagicData.StrategySummary buyStrategy in Model.Summary.BuyStrategies) { - - - - - - - buyStrategyIndex++; - } - } - -
Buy Strat.@Model.Summary.BuyStrategyBuy Value@Model.Summary.BuyValue.ToString(new System.Globalization.CultureInfo("en-US"))
Buy Strat. @buyStrategyIndex@buyStrategy.NameBuy Value @buyStrategyIndex@buyStrategy.Value.ToString(new System.Globalization.CultureInfo("en-US"))
-
- -
-

Active Sell Strategies

- - - - @if (Model.Summary.SellStrategies.Count == 0) { - - - - - - - } else { - char sellStrategyIndex = 'A'; - foreach (Core.Main.DataObjects.PTMagicData.StrategySummary sellStrategy in Model.Summary.SellStrategies) { - - - - - - - sellStrategyIndex++; - } - } - -
Sell Strat.@Model.Summary.SellStrategySell Value@Model.Summary.SellValue.ToString(new System.Globalization.CultureInfo("en-US"))
Sell Strat. @sellStrategyIndex@sellStrategy.NameSell Value @sellStrategyIndex@sellStrategy.Value.ToString(new System.Globalization.CultureInfo("en-US"))
-
-
+
diff --git a/Monitor/wwwroot/assets/css/style.css b/Monitor/wwwroot/assets/css/style.css index a81c70b..6eb5cfe 100644 --- a/Monitor/wwwroot/assets/css/style.css +++ b/Monitor/wwwroot/assets/css/style.css @@ -639,7 +639,7 @@ a.text-dark:hover { } .bg-success { - background-color: #81c868 !important; + background-color: #296a12 !important; } .bg-info { @@ -647,11 +647,11 @@ a.text-dark:hover { } .bg-warning { - background-color: #ffbd4a !important; + background-color: #563a09 !important; } .bg-danger { - background-color: #f05050 !important; + background-color: #6e0e0e !important; } .bg-muted { @@ -759,7 +759,7 @@ a.text-dark:hover { } .label-success { - background-color: #81c868; + background-color: #3b5e2f; } .label-info { @@ -767,11 +767,11 @@ a.text-dark:hover { } .label-warning { - background-color: #ffbd4a; + background-color: #847f0a; } .label-danger { - background-color: #f05050; + background-color: #601f1f; } .label-purple { @@ -829,7 +829,7 @@ a.text-dark:hover { } .badge-success { - background-color: #81c868; + background-color: #307516; } .badge-info { @@ -837,11 +837,11 @@ a.text-dark:hover { } .badge-warning { - background-color: #ffbd4a; + background-color: #6f5019; } .badge-danger { - background-color: #f05050; + background-color: #6a1414; } .badge-purple { diff --git a/PTMagic.sln b/PTMagic.sln new file mode 100644 index 0000000..1f9a9e3 --- /dev/null +++ b/PTMagic.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{DC985FA9-87CC-4C60-A587-E646E74A1A4A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monitor", "Monitor\Monitor.csproj", "{D967C466-E4BF-40A9-84FD-698079FAD3D5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PTMagic", "PTMagic\PTMagic.csproj", "{D81F5541-438E-42C7-B6E7-B859875B18A8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Release|Any CPU.Build.0 = Release|Any CPU + {D967C466-E4BF-40A9-84FD-698079FAD3D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D967C466-E4BF-40A9-84FD-698079FAD3D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D967C466-E4BF-40A9-84FD-698079FAD3D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D967C466-E4BF-40A9-84FD-698079FAD3D5}.Release|Any CPU.Build.0 = Release|Any CPU + {D81F5541-438E-42C7-B6E7-B859875B18A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D81F5541-438E-42C7-B6E7-B859875B18A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D81F5541-438E-42C7-B6E7-B859875B18A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D81F5541-438E-42C7-B6E7-B859875B18A8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C3646F7E-91F6-4D66-9B1B-2B74317580A4} + EndGlobalSection +EndGlobal diff --git a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json index 4f9bb22..beb9ffe 100644 --- a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json +++ b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json @@ -12,11 +12,11 @@ "Exchange": "Bittrex", // The exchange your are running Profit Trailer on "StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer "TimezoneOffset": "+0:00", // Your timezone offset from UTC time - "MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor + //"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them "CoinMarketCapAPIKey": "", //CoinMarketCap Api - "FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key + //"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key }, "Monitor": { "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start @@ -28,6 +28,7 @@ "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers "RefreshSeconds": 30, // The refresh interval of your monitor main page "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it + "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart. "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer "MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer "MaxMonthlySummaries": 10, // The amount of "Last Months" being shown in your Sales Analyzer diff --git a/_Development/DevSettings/settings.general.json b/_Development/DevSettings/settings.general.json index 4f9bb22..beb9ffe 100644 --- a/_Development/DevSettings/settings.general.json +++ b/_Development/DevSettings/settings.general.json @@ -12,11 +12,11 @@ "Exchange": "Bittrex", // The exchange your are running Profit Trailer on "StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer "TimezoneOffset": "+0:00", // Your timezone offset from UTC time - "MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor + //"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them "CoinMarketCapAPIKey": "", //CoinMarketCap Api - "FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key + //"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key }, "Monitor": { "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start @@ -28,6 +28,7 @@ "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers "RefreshSeconds": 30, // The refresh interval of your monitor main page "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it + "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart. "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer "MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer "MaxMonthlySummaries": 10, // The amount of "Last Months" being shown in your Sales Analyzer From bde9b98f11da7c8eeb20d4cada92d9ccd1206cf2 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Sat, 30 Dec 2023 14:46:45 +0900 Subject: [PATCH 02/14] Dashboard Bottom Sales Overview changes --- Core/DataObjects/PTMagicData.cs | 25 +++ Core/DataObjects/ProfitTrailerData.cs | 177 +++++++++++-------- Monitor/Pages/_get/DashboardBottom.cshtml | 110 +++++++----- Monitor/Pages/_get/DashboardBottom.cshtml.cs | 3 + 4 files changed, 195 insertions(+), 120 deletions(-) diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index fa6f69b..a7d6bde 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -431,6 +431,31 @@ namespace Core.Main.DataObjects.PTMagicData public double TotalCost { get; set; } public double SoldPrice { get; set; } public double SoldValue { get; set; } + public double TotalSales { get; set; } + } + + public class StatsData + { + public double SalesToday { get; set; } + public double ProfitToday { get; set; } + public double ProfitPercToday { get; set; } + public double SalesYesterday { get; set; } + public double ProfitYesterday { get; set; } + public double ProfitPercYesterday { get; set; } + public double SalesWeek { get; set; } + public double ProfitWeek { get; set; } + public double ProfitPercWeek { get; set; } + public double SalesMonth { get; set; } + public double ProfitMonth { get; set; } + public double ProfitPercMonth { get; set; } + public double TotalProfit { get; set; } + public double TotalSales { get; set; } + public double TotalProfitPerc { get; set; } + public double FundingToday { get; set; } + public double FundingYesterday { get; set; } + public double FundingWeek { get; set; } + public double FundingMonth { get; set; } + public double FundingTotal { get; set; } } public class PTStrategy diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index ca04707..10b8231 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -16,16 +16,22 @@ namespace Core.Main.DataObjects { private SummaryData _summary = null; private Properties _properties = null; + private List _stats = null; private List _sellLog = new List(); private List _dcaLog = new List(); private List _buyLog = new List(); private string _ptmBasePath = ""; private PTMagicConfiguration _systemConfiguration = null; private TransactionData _transactionData = null; - private DateTime _buyLogRefresh = DateTime.UtcNow, _sellLogRefresh = DateTime.UtcNow, _dcaLogRefresh = DateTime.UtcNow, _summaryRefresh = DateTime.UtcNow, _propertiesRefresh = DateTime.UtcNow; - private volatile object _buyLock = new object(), _sellLock = new object(), _dcaLock = new object(), _summaryLock = new object(), _propertiesLock = new object(); + private DateTime _statsRefresh = DateTime.UtcNow,_buyLogRefresh = DateTime.UtcNow, _sellLogRefresh = DateTime.UtcNow, _dcaLogRefresh = DateTime.UtcNow, _summaryRefresh = DateTime.UtcNow, _propertiesRefresh = DateTime.UtcNow; + private volatile object _statsLock = new object(),_buyLock = new object(), _sellLock = new object(), _dcaLock = new object(), _summaryLock = new object(), _propertiesLock = new object(); private TimeSpan? _offsetTimeSpan = null; - + + public void DoLog(string message) + { + // Implement your logging logic here + Console.WriteLine(message); + } // Constructor public ProfitTrailerData(PTMagicConfiguration systemConfiguration) { @@ -96,86 +102,82 @@ namespace Core.Main.DataObjects return _properties; } } + + public List Stats + { + get + { + if (_stats == null || DateTime.UtcNow > _statsRefresh) + { + lock (_statsLock) + { + if (_stats == null || DateTime.UtcNow > _statsRefresh) + { + dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); + JObject statsDataJObject = statsDataJson as JObject; + JObject basicSection = (JObject)statsDataJObject["basic"]; + _stats = new List { BuildStatsData(basicSection) }; + _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + return _stats; + } + } + + public List SellLog { get { + if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) { - lock (_sellLock) - { - // Thread double locking - if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) + lock (_sellLock) { - _sellLog.Clear(); - - // Page through the sales data summarizing it. - bool exitLoop = false; - int pageIndex = 1; - - int maxPages = _systemConfiguration.GeneralSettings.Monitor.MaxSalesRecords; - int requestedPages = 0; - - while (!exitLoop && requestedPages < maxPages) + // Thread double locking + if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) { - var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?Page=1&perPage=1&sort=SOLDDATE&sortDirection=DESCENDING&page=" + pageIndex); - if (sellDataPage != null && sellDataPage.data.Count > 0) - { - // Add sales data page to collection - this.BuildSellLogData(sellDataPage); - pageIndex++; - requestedPages++; + _sellLog.Clear(); + - } - else + // Page through the sales data summarizing it. + bool exitLoop = false; + int pageIndex = 1; + + // 1 record per page to allow user to set max records to retrieve + int maxPages = _systemConfiguration.GeneralSettings.Monitor.MaxSalesRecords; + int requestedPages = 0; + + while (!exitLoop && requestedPages < maxPages) { - // All data retrieved - exitLoop = true; + var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?Page=1&perPage=1&sort=SOLDDATE&sortDirection=DESCENDING&page=" + pageIndex); + if (sellDataPage != null && sellDataPage.data.Count > 0) + { + // Add sales data page to collection + this.BuildSellLogData(sellDataPage); + pageIndex++; + requestedPages++; + Console.WriteLine($"Importing sale: {pageIndex}"); + + } + else + { + // All data retrieved + exitLoop = true; + } } + + // Update sell log refresh time + _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds -1); } - - // Update sell log refresh time - _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds -1); } } - } - return _sellLog; } } - public List SellLogToday - { - get - { - return SellLog.FindAll(sl => sl.SoldDate.Date == LocalizedTime.DateTime.Date); - } - } - - public List SellLogYesterday - { - get - { - return SellLog.FindAll(sl => sl.SoldDate.Date == LocalizedTime.DateTime.AddDays(-1).Date); - } - } - - public List SellLogLast7Days - { - get - { - return SellLog.FindAll(sl => sl.SoldDate.Date >= LocalizedTime.DateTime.AddDays(-7).Date); - } - } - - public List SellLogLast30Days - { - get - { - return SellLog.FindAll(sl => sl.SoldDate.Date >= LocalizedTime.DateTime.AddDays(-30).Date); - } - } - + public List DCALog { get @@ -313,15 +315,18 @@ namespace Core.Main.DataObjects response.Close(); - // Parse the JSON and build the data sets + if (!arrayReturned) - { - return JObject.Parse(rawBody); - } - else - { - return JArray.Parse(rawBody); - } + { + return JObject.Parse(rawBody); + } + else + { + return JArray.Parse(rawBody); + } + + + } private SummaryData BuildSummaryData(dynamic PTData) @@ -349,6 +354,32 @@ namespace Core.Main.DataObjects BaseUrl = PTProperties.baseUrl }; } + private StatsData BuildStatsData(dynamic statsDataJson) + { + return new StatsData() + { + SalesToday = statsDataJson["totalSalesToday"], + ProfitToday = statsDataJson["totalProfitToday"], + ProfitPercToday = statsDataJson["totalProfitPercToday"], + SalesYesterday = statsDataJson["totalSalesYesterday"], + ProfitYesterday = statsDataJson["totalProfitYesterday"], + ProfitPercYesterday = statsDataJson["totalProfitPercYesterday"], + SalesWeek = statsDataJson["totalSalesWeek"], + ProfitWeek = statsDataJson["totalProfitWeek"], + ProfitPercWeek = statsDataJson["totalProfitPercWeek"], + SalesMonth = statsDataJson["totalSalesThisMonth"], + ProfitMonth = statsDataJson["totalProfitThisMonth"], + ProfitPercMonth = statsDataJson["totalProfitPercThisMonth"], + TotalProfit = statsDataJson["totalProfit"], + TotalSales = statsDataJson["totalSales"], + TotalProfitPerc = statsDataJson["totalProfitPerc"], + FundingToday = statsDataJson["totalFundingToday"], + FundingYesterday = statsDataJson["totalFundingYesterday"], + FundingWeek = statsDataJson["totalFundingWeek"], + FundingMonth = statsDataJson["totalFundingThisMonth"], + FundingTotal = statsDataJson["totalFunding"] + }; + } private void BuildSellLogData(dynamic rawSellLogData) { foreach (var rsld in rawSellLogData.data) diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index 823b6f6..d15c6d4 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -62,7 +62,7 @@
-
+

@@ -113,46 +113,56 @@
-
+

Sales Overviewmore

@{ - double totalProfit = 0; - totalProfit = Model.PTData.SellLog.Sum(s => s.Profit); - double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2); - double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2); - string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; - if (Model.PTData.TransactionData.Transactions.Count > 0) - { - percentGainText = ""; - } - double todaysProfit = 0; - todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit); - double todaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime); - double todaysProfitFiat = Math.Round(todaysProfit * Model.Summary.MainMarketPrice, 2); - double todaysPercentGain = Math.Round(todaysProfit / todaysStartBalance * 100, 2); + var overviewStats = Model.StatsData.FirstOrDefault(); // todaysStats is a new variable + + var todaysSales = overviewStats.SalesToday; + var todaysProfit = overviewStats.ProfitToday; + var todaysFunding = overviewStats.FundingToday; + var todaysPercentGain = overviewStats.ProfitPercToday; + var todaysFundingGain = todaysPercentGain * ((todaysProfit - todaysFunding) / todaysProfit); + + + var yesterdaysSales = overviewStats.SalesYesterday; + var yesterdaysProfit = overviewStats.ProfitYesterday; + var yesterdaysFunding = overviewStats.FundingYesterday; + var yesterdaysPercentGain = overviewStats.ProfitPercYesterday; + var yesterdaysFundingGain = yesterdaysPercentGain * ((yesterdaysProfit + yesterdaysFunding) / yesterdaysProfit); + + var last7DaysSales = overviewStats.SalesWeek; + var last7DaysProfit = overviewStats.ProfitWeek; + var last7DaysFunding = overviewStats.FundingWeek; + var last7DaysPercentGain = overviewStats.ProfitPercWeek; + var last7DaysFundingGain = last7DaysPercentGain * ((last7DaysProfit + last7DaysFunding) / last7DaysProfit); + + var last30DaysSales = overviewStats.SalesMonth; + var last30DaysProfit = overviewStats.ProfitMonth; + var last30DaysFunding = overviewStats.FundingMonth; + var last30DaysPercentGain = overviewStats.ProfitPercMonth; + var last30DaysFundingGain = last30DaysPercentGain * ((last30DaysProfit + last30DaysFunding) / last30DaysProfit); + + var totalSales = overviewStats.TotalSales; + var totalProfit = overviewStats.TotalProfit; + var totalFunding = overviewStats.FundingTotal; + var totalProfitPercent = overviewStats.TotalProfitPerc; + var totalFundingGain = totalProfitPercent * ((totalProfit + totalFunding) / totalProfit); + + double todaysProfitFiat = Math.Round((todaysProfit + todaysFunding) * Model.Summary.MainMarketPrice, 2); + double yesterdaysProfitFiat = Math.Round((yesterdaysProfit + yesterdaysFunding) * Model.Summary.MainMarketPrice, 2); + double last7DaysProfitFiat = Math.Round((last7DaysProfit + last7DaysFunding) * Model.Summary.MainMarketPrice, 2); + double last30DaysProfitFiat = Math.Round((last30DaysProfit + last30DaysFunding) * Model.Summary.MainMarketPrice, 2); + double totalProfitFiat = Math.Round((totalProfit + totalFunding) * Model.Summary.MainMarketPrice, 2); + - double yesterdaysProfit = 0; - yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit); - double yesterdaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-1)); - double yesterdaysProfitFiat = Math.Round(yesterdaysProfit * Model.Summary.MainMarketPrice, 2); - double yesterdaysPercentGain = Math.Round(yesterdaysProfit / yesterdaysStartBalance * 100, 2); - double last7DaysProfit = 0; - last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit); - double last7DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-7)); - double last7DaysProfitFiat = Math.Round(last7DaysProfit * Model.Summary.MainMarketPrice, 2); - double last7DaysPercentGain = Math.Round(last7DaysProfit / last7DaysStartBalance * 100, 2); - double last30DaysProfit = 0; - last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit); - double last30DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-30)); - double last30DaysProfitFiat = Math.Round(last30DaysProfit * Model.Summary.MainMarketPrice, 2); - double last30DaysPercentGain = Math.Round(last30DaysProfit / last30DaysStartBalance * 100, 2); } @@ -160,45 +170,51 @@ - + + - + - - + + + - + - - + + + - + - - + + + - + - - + + + - + - - + + +
Sales Profit @Model.Summary.MainMarketProfit @Model.Summary.MainFiatCurrencyFunding@Model.Summary.MainFiatCurrency Gain
Today@Model.PTData.SellLogToday.Count@overviewStats.SalesToday @todaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Model.MainFiatCurrencySymbol + todaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@todaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@todaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(todaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@todaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Yesterday@Model.PTData.SellLogYesterday.Count@yesterdaysSales @yesterdaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Model.MainFiatCurrencySymbol + yesterdaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@yesterdaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@yesterdaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(yesterdaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@yesterdaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Last 7 Days@Model.PTData.SellLogLast7Days.Count@last7DaysSales @last7DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Model.MainFiatCurrencySymbol + last7DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@last7DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@last7DaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(last7DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@last7DaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Last 30 Days@Model.PTData.SellLogLast30Days.Count@last30DaysSales @last30DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Model.MainFiatCurrencySymbol + last30DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@last30DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@last30DaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(last30DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@last30DaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Total@Model.PTData.SellLog.Count@totalSales @totalProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Model.MainFiatCurrencySymbol + totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@Html.Raw(percentGainText)@totalFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@totalFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index 0ff5157..3f5dbea 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -13,6 +13,7 @@ namespace Monitor.Pages public class DashboardBottomModel : _Internal.BasePageModelSecureAJAX { public ProfitTrailerData PTData = null; + public List StatsData { get; set; } public List MarketTrends { get; set; } = new List(); public string TrendChartDataJSON = ""; public string ProfitChartDataJSON = ""; @@ -26,12 +27,14 @@ namespace Monitor.Pages base.Init(); BindData(); + BuildAssetDistributionData(); } private void BindData() { PTData = this.PtDataObject; + StatsData = this.PTData.Stats; // Cleanup temp files FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5); From 28c12e6d5a1c7be0fac390d9511d592b4fd5fbe3 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Mon, 8 Jan 2024 06:53:49 +0900 Subject: [PATCH 03/14] Dashboard Bottom --- Core/DataObjects/PTMagicData.cs | 98 +++--- Core/DataObjects/ProfitTrailerData.cs | 301 +++++++++++------- Core/Main/PTMagic.cs | 12 +- Monitor/Pages/Index.cshtml | 19 +- Monitor/Pages/SalesAnalyzer.cshtml | 4 +- Monitor/Pages/SalesAnalyzer.cshtml.cs | 2 + Monitor/Pages/SettingsGeneral.cshtml | 6 +- Monitor/Pages/SettingsGeneral.cshtml.cs | 11 +- Monitor/Pages/_get/DashboardBottom.cshtml | 256 +++++++++------ Monitor/Pages/_get/DashboardBottom.cshtml.cs | 182 +++++------ .../settings.general.json | 1 - .../DevSettings/settings.general.json | 1 - 12 files changed, 521 insertions(+), 372 deletions(-) diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index a7d6bde..298c2bf 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Text; namespace Core.Main.DataObjects.PTMagicData { - #region Settings Objects public class GeneralSettingsWrapper { public GeneralSettings GeneralSettings { get; set; } @@ -21,7 +19,7 @@ namespace Core.Main.DataObjects.PTMagicData public SecureSettings SecureSettings { get; set; } } - #region GeneralSettings + public class GeneralSettings { public Application Application { get; set; } @@ -43,7 +41,7 @@ namespace Core.Main.DataObjects.PTMagicData public string ProfitTrailerDefaultSettingName { get; set; } = "default"; public int FloodProtectionMinutes { get; set; } = 15; public string Exchange { get; set; } - public double StartBalance { get; set; } = 0; + //public double StartBalance { get; set; } = 0; public string InstanceName { get; set; } = "PT Magic"; public string TimezoneOffset { get; set; } = "+0:00"; public string MainFiatCurrency { get; set; } = "USD"; @@ -106,9 +104,7 @@ namespace Core.Main.DataObjects.PTMagicData public Int64 ChatId { get; set; } public bool SilentMode { get; set; } = false; } - #endregion - #region AnalyzerSettings public class AnalyzerSettings { public MarketAnalyzer MarketAnalyzer { get; set; } @@ -244,18 +240,12 @@ namespace Core.Main.DataObjects.PTMagicData [DefaultValue(0)] public int HoursSinceTriggered { get; set; } = 0; } - #endregion - #region SecureSettings public class SecureSettings { public string MonitorPassword { get; set; } = ""; } - #endregion - #endregion - - #region Market Analyzer Objects public class Market { public int Position; @@ -291,9 +281,7 @@ namespace Core.Main.DataObjects.PTMagicData public DateTime FirstSeen = Constants.confMinDate; public DateTime LastSeen = Constants.confMaxDate; } - #endregion - - #region Summary Objects + public class Summary { public string Version { get; set; } = ""; @@ -324,14 +312,26 @@ namespace Core.Main.DataObjects.PTMagicData public string SellStrategy { get; set; } = ""; public string MainMarket { get; set; } = ""; public double MainMarketPrice { get; set; } = 0; - public string MainFiatCurrency { get; set; } = "USD"; - public double MainFiatCurrencyExchangeRate { get; set; } = 1; + private PropertiesData _propertiesData = new PropertiesData(); + public string MainFiatCurrency => _propertiesData.Currency; + private SummaryData _summaryData = new SummaryData(); + public double MainFiatCurrencyExchangeRate => _summaryData.FiatConversionRate; public List BuyStrategies { get; set; } = new List(); public List SellStrategies { get; set; } = new List(); public List DCABuyStrategies { get; set; } = new List(); public List DCASellStrategies { get; set; } = new List(); } - + public class PropertiesData + { + public string Currency { get; set; } = ""; + public bool Shorting { get; set; } = false; + public bool Margin { get; set; } = false; + public string UpTime { get; set; } = ""; + public int Port { get; set; } = 0; + public bool IsLeverageExchange { get; set; } = false; + public string BaseUrl { get; set; } = ""; + } + public class StrategySummary { public string Name { get; set; } = ""; @@ -364,22 +364,7 @@ namespace Core.Main.DataObjects.PTMagicData public List DCABuyStrategies { get; set; } = new List(); public List DCASellStrategies { get; set; } = new List(); } - #endregion - - #region Properties Objects - public class Properties - { - public string Currency { get; set; } = ""; - public bool Shorting { get; set; } = false; - public bool Margin { get; set; } = false; - public string UpTime { get; set; } = ""; - public int Port { get; set; } = 0; - public bool IsLeverageExchange { get; set; } = false; - public string BaseUrl { get; set; } = ""; - } - #endregion - - #region Transaction Objects + public class Transaction { public string GUID { get; set; } = ""; @@ -397,9 +382,7 @@ namespace Core.Main.DataObjects.PTMagicData return result.DateTime; } } - #endregion - - #region SingleMarketSettingSummary Objects + public class SingleMarketSettingSummary { public string Market { get; set; } = ""; @@ -415,11 +398,8 @@ namespace Core.Main.DataObjects.PTMagicData public double LastPrice { get; set; } = 0; public double Last24hVolume { get; set; } = 0; } - #endregion - - #region Profit Trailer JSON Objects - - public class SellLogData + + public class SellLogData { public double SoldAmount { get; set; } public DateTime SoldDate { get; set; } @@ -445,19 +425,39 @@ namespace Core.Main.DataObjects.PTMagicData public double SalesWeek { get; set; } public double ProfitWeek { get; set; } public double ProfitPercWeek { get; set; } - public double SalesMonth { get; set; } - public double ProfitMonth { get; set; } - public double ProfitPercMonth { get; set; } + public double SalesThisMonth { get; set; } + public double ProfitThisMonth { get; set; } + public double ProfitPercThisMonth { get; set; } + public double SalesLastMonth { get; set; } + public double ProfitLastMonth { get; set; } + public double ProfitPercLastMonth { get; set; } public double TotalProfit { get; set; } public double TotalSales { get; set; } public double TotalProfitPerc { get; set; } public double FundingToday { get; set; } public double FundingYesterday { get; set; } public double FundingWeek { get; set; } - public double FundingMonth { get; set; } + public double FundingThisMonth { get; set; } + public double FundingLastMonth { get; set; } public double FundingTotal { get; set; } } + public class DailyStatsData + { + public string Date { get; set; } + public double TotalSales { get; set; } + public double TotalBuys { get; set; } + public double TotalProfitCurrency { get; set; } + public int Order { get; set; } + } + + public class DailyPNLData + { + public string Date { get; set; } + public double CumulativeProfitCurrency { get; set; } + public double Order { get; set; } + } + public class PTStrategy { public string type { get; set; } @@ -534,15 +534,13 @@ namespace Core.Main.DataObjects.PTMagicData } public class SummaryData - { - public double Balance { get; set; } + { public double Balance { get; set; } public double StartBalance { get; set; } + public double FiatConversionRate { get; set; } public double PairsValue { get; set; } public double DCAValue { get; set; } public double PendingValue { get; set; } public double DustValue { get; set; } public string Market { get; set; } } - - #endregion } \ No newline at end of file diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 10b8231..465a205 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -15,18 +15,32 @@ namespace Core.Main.DataObjects public class ProfitTrailerData { private SummaryData _summary = null; - private Properties _properties = null; - private List _stats = null; + private PropertiesData _properties = null; + private StatsData _stats = null; + private List _dailyStats = new List(); + private List _dailyPNL = new List(); private List _sellLog = new List(); private List _dcaLog = new List(); private List _buyLog = new List(); private string _ptmBasePath = ""; private PTMagicConfiguration _systemConfiguration = null; private TransactionData _transactionData = null; - private DateTime _statsRefresh = DateTime.UtcNow,_buyLogRefresh = DateTime.UtcNow, _sellLogRefresh = DateTime.UtcNow, _dcaLogRefresh = DateTime.UtcNow, _summaryRefresh = DateTime.UtcNow, _propertiesRefresh = DateTime.UtcNow; - private volatile object _statsLock = new object(),_buyLock = new object(), _sellLock = new object(), _dcaLock = new object(), _summaryLock = new object(), _propertiesLock = new object(); - private TimeSpan? _offsetTimeSpan = null; - + private DateTime _dailyStatsRefresh = DateTime.UtcNow; + private DateTime _dailyPNLRefresh = DateTime.UtcNow; + private DateTime _statsRefresh = DateTime.UtcNow; + private DateTime _buyLogRefresh = DateTime.UtcNow; + private DateTime _sellLogRefresh = DateTime.UtcNow; + private DateTime _dcaLogRefresh = DateTime.UtcNow; + private DateTime _summaryRefresh = DateTime.UtcNow; + private DateTime _propertiesRefresh = DateTime.UtcNow; + private volatile object _dailyStatsLock = new object(); + private volatile object _dailyPNLLock = new object(); + private volatile object _statsLock = new object(); + private volatile object _buyLock = new object(); + private volatile object _sellLock = new object(); + private volatile object _dcaLock = new object(); + private volatile object _summaryLock = new object(); + private volatile object _propertiesLock = new object(); private TimeSpan? _offsetTimeSpan = null; public void DoLog(string message) { // Implement your logging logic here @@ -78,11 +92,25 @@ namespace Core.Main.DataObjects } } } - + return _summary; } } - public Properties Properties + private SummaryData BuildSummaryData(dynamic PTData) + { + return new SummaryData() + { + Market = PTData.market, + FiatConversionRate = PTData.priceDataFiatConversionRate, + Balance = PTData.realBalance, + PairsValue = PTData.totalPairsCurrentValue, + DCAValue = PTData.totalDCACurrentValue, + PendingValue = PTData.totalPendingCurrentValue, + DustValue = PTData.totalDustCurrentValue, + StartBalance = PTData.startBalance, + }; + } + public PropertiesData Properties { get { @@ -102,8 +130,20 @@ namespace Core.Main.DataObjects return _properties; } } - - public List Stats + private PropertiesData BuildProptertiesData(dynamic PTProperties) + { + return new PropertiesData() + { + Currency = PTProperties.currency, + Shorting = PTProperties.shorting, + Margin = PTProperties.margin, + UpTime = PTProperties.upTime, + Port = PTProperties.port, + IsLeverageExchange = PTProperties.isLeverageExchange, + BaseUrl = PTProperties.baseUrl + }; + } + public StatsData Stats { get { @@ -116,7 +156,7 @@ namespace Core.Main.DataObjects dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); JObject statsDataJObject = statsDataJson as JObject; JObject basicSection = (JObject)statsDataJObject["basic"]; - _stats = new List { BuildStatsData(basicSection) }; + _stats = BuildStatsData(basicSection); _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); } } @@ -124,13 +164,103 @@ namespace Core.Main.DataObjects return _stats; } } - - + private StatsData BuildStatsData(dynamic statsDataJson) + { + return new StatsData() + { + SalesToday = statsDataJson["totalSalesToday"], + ProfitToday = statsDataJson["totalProfitToday"], + ProfitPercToday = statsDataJson["totalProfitPercToday"], + SalesYesterday = statsDataJson["totalSalesYesterday"], + ProfitYesterday = statsDataJson["totalProfitYesterday"], + ProfitPercYesterday = statsDataJson["totalProfitPercYesterday"], + SalesWeek = statsDataJson["totalSalesWeek"], + ProfitWeek = statsDataJson["totalProfitWeek"], + ProfitPercWeek = statsDataJson["totalProfitPercWeek"], + SalesThisMonth = statsDataJson["totalSalesThisMonth"], + ProfitThisMonth = statsDataJson["totalProfitThisMonth"], + ProfitPercThisMonth = statsDataJson["totalProfitPercThisMonth"], + SalesLastMonth = statsDataJson["totalSalesLastMonth"], + ProfitLastMonth = statsDataJson["totalProfitLastMonth"], + ProfitPercLastMonth = statsDataJson["totalProfitPercLastMonth"], + TotalProfit = statsDataJson["totalProfit"], + TotalSales = statsDataJson["totalSales"], + TotalProfitPerc = statsDataJson["totalProfitPerc"], + FundingToday = statsDataJson["totalFundingToday"], + FundingYesterday = statsDataJson["totalFundingYesterday"], + FundingWeek = statsDataJson["totalFundingWeek"], + FundingThisMonth = statsDataJson["totalFundingThisMonth"], + FundingLastMonth = statsDataJson["totalFundingLastMonth"], + FundingTotal = statsDataJson["totalFunding"] + }; + } + public List DailyStats + { + get + { + if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) + { + lock (_dailyStatsLock) + { + if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) + { + dynamic dailyStatsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); + JObject dailyStatsDataJObject = dailyStatsDataJson as JObject; + JArray dailyStatsSection = (JArray)dailyStatsDataJObject["extra"]["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"], + TotalProfitCurrency = dailyStatsDataJson["totalProfitCurrency"], + Order = dailyStatsDataJson["order"], + }; + } + public List DailyPNL + { + get + { + if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + { + lock (_dailyPNLLock) + { + if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + { + dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); + JObject dailyPNLDataJObject = dailyPNLDataJson as JObject; + JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"]; + _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); + _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + return _dailyPNL; + } + } + private DailyPNLData BuildDailyPNLData(dynamic dailyPNLDataJson) + { + return new DailyPNLData() + { + Date = dailyPNLDataJson["date"], + CumulativeProfitCurrency = dailyPNLDataJson["cumulativeProfitCurrency"], + Order = dailyPNLDataJson["order"], + }; + } public List SellLog { get { - + if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) { lock (_sellLock) @@ -139,16 +269,16 @@ namespace Core.Main.DataObjects if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) { _sellLog.Clear(); - + // Page through the sales data summarizing it. bool exitLoop = false; int pageIndex = 1; - + // 1 record per page to allow user to set max records to retrieve int maxPages = _systemConfiguration.GeneralSettings.Monitor.MaxSalesRecords; int requestedPages = 0; - + while (!exitLoop && requestedPages < maxPages) { var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?Page=1&perPage=1&sort=SOLDDATE&sortDirection=DESCENDING&page=" + pageIndex); @@ -167,7 +297,7 @@ namespace Core.Main.DataObjects exitLoop = true; } } - + // Update sell log refresh time _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds -1); } @@ -177,7 +307,7 @@ namespace Core.Main.DataObjects } } - + public List DCALog { get @@ -203,7 +333,6 @@ namespace Core.Main.DataObjects () => { pendingData = GetDataFromProfitTrailer("/api/v2/data/pending", true); - }, () => { @@ -276,20 +405,21 @@ namespace Core.Main.DataObjects return (this.Summary.DustValue); } - + + public double GetSnapshotBalance(DateTime snapshotDateTime) { - double result = _systemConfiguration.GeneralSettings.Application.StartBalance; - + double result = _summary.StartBalance; + result += this.SellLog.FindAll(sl => sl.SoldDate.Date < snapshotDateTime.Date).Sum(sl => sl.Profit); result += this.TransactionData.Transactions.FindAll(t => t.UTCDateTime < snapshotDateTime).Sum(t => t.Amount); - + // Calculate holdings for snapshot date result += this.DCALog.FindAll(pairs => pairs.FirstBoughtDate <= snapshotDateTime).Sum(pairs => pairs.CurrentValue); - + return result; } - + private dynamic GetDataFromProfitTrailer(string callPath, bool arrayReturned = false) { string rawBody = ""; @@ -297,15 +427,15 @@ namespace Core.Main.DataObjects callPath, callPath.Contains("?") ? "&" : "?", _systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken); - + // Get the data from PT Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url)); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.AutomaticDecompression = DecompressionMethods.GZip; request.KeepAlive = true; - + WebResponse response = request.GetResponse(); - + using (Stream dataStream = response.GetResponseStream()) { StreamReader reader = new StreamReader(dataStream); @@ -314,8 +444,8 @@ namespace Core.Main.DataObjects } response.Close(); - - + + if (!arrayReturned) { return JObject.Parse(rawBody); @@ -324,62 +454,9 @@ namespace Core.Main.DataObjects { return JArray.Parse(rawBody); } - - - } - private SummaryData BuildSummaryData(dynamic PTData) - { - return new SummaryData() - { - Market = PTData.market, - Balance = PTData.realBalance, - PairsValue = PTData.totalPairsCurrentValue, - DCAValue = PTData.totalDCACurrentValue, - PendingValue = PTData.totalPendingCurrentValue, - DustValue = PTData.totalDustCurrentValue - }; - } - private Properties BuildProptertiesData(dynamic PTProperties) - { - return new Properties() - { - Currency = PTProperties.currency, - Shorting = PTProperties.shorting, - Margin = PTProperties.margin, - UpTime = PTProperties.upTime, - Port = PTProperties.port, - IsLeverageExchange = PTProperties.isLeverageExchange, - BaseUrl = PTProperties.baseUrl - }; - } - private StatsData BuildStatsData(dynamic statsDataJson) - { - return new StatsData() - { - SalesToday = statsDataJson["totalSalesToday"], - ProfitToday = statsDataJson["totalProfitToday"], - ProfitPercToday = statsDataJson["totalProfitPercToday"], - SalesYesterday = statsDataJson["totalSalesYesterday"], - ProfitYesterday = statsDataJson["totalProfitYesterday"], - ProfitPercYesterday = statsDataJson["totalProfitPercYesterday"], - SalesWeek = statsDataJson["totalSalesWeek"], - ProfitWeek = statsDataJson["totalProfitWeek"], - ProfitPercWeek = statsDataJson["totalProfitPercWeek"], - SalesMonth = statsDataJson["totalSalesThisMonth"], - ProfitMonth = statsDataJson["totalProfitThisMonth"], - ProfitPercMonth = statsDataJson["totalProfitPercThisMonth"], - TotalProfit = statsDataJson["totalProfit"], - TotalSales = statsDataJson["totalSales"], - TotalProfitPerc = statsDataJson["totalProfitPerc"], - FundingToday = statsDataJson["totalFundingToday"], - FundingYesterday = statsDataJson["totalFundingYesterday"], - FundingWeek = statsDataJson["totalFundingWeek"], - FundingMonth = statsDataJson["totalFundingThisMonth"], - FundingTotal = statsDataJson["totalFunding"] - }; - } + private void BuildSellLogData(dynamic rawSellLogData) { foreach (var rsld in rawSellLogData.data) @@ -393,20 +470,23 @@ namespace Core.Main.DataObjects sellLogData.AverageBuyPrice = rsld.avgPrice; sellLogData.TotalCost = rsld.totalCost; sellLogData.Profit = rsld.profitCurrency; - - + + //Convert Unix Timestamp to Datetime System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc); dtDateTime = dtDateTime.AddSeconds((double)rsld.soldDate).ToUniversalTime(); - + + // Profit Trailer sales are saved in UTC DateTimeOffset ptSoldDate = DateTimeOffset.Parse(dtDateTime.Year.ToString() + "-" + dtDateTime.Month.ToString("00") + "-" + dtDateTime.Day.ToString("00") + "T" + dtDateTime.Hour.ToString("00") + ":" + dtDateTime.Minute.ToString("00") + ":" + dtDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); - + + // Convert UTC sales time to local offset time + ptSoldDate = ptSoldDate.ToOffset(OffsetTimeSpan); - + sellLogData.SoldDate = ptSoldDate.DateTime; - + _sellLog.Add(sellLogData); } } @@ -415,23 +495,22 @@ namespace Core.Main.DataObjects { // Parse DCA data _dcaLog.AddRange(ParsePairsData(rawDCALogData, true)); - + // Parse Pairs data _dcaLog.AddRange(ParsePairsData(rawPairsLogData, false)); - + // Parse pending pairs data _dcaLog.AddRange(ParsePairsData(rawPendingLogData, false)); - + // Parse watch only pairs data _dcaLog.AddRange(ParsePairsData(rawWatchModeLogData, false)); - } // Parse the pairs data from PT to our own common data structure. private List ParsePairsData(dynamic pairsData, bool processBuyStrategies) { List pairs = new List(); - + foreach (var pair in pairsData) { DCALogData dcaLogData = new DCALogData(); @@ -452,7 +531,7 @@ namespace Core.Main.DataObjects dcaLogData.BuyStrategy = pair.buyStrategy == null ? "" : pair.buyStrategy; dcaLogData.SellStrategy = pair.sellStrategy == null ? "" : pair.sellStrategy; dcaLogData.IsTrailing = false; - + // See if they are using PT 2.5 (buyStrategiesData) or 2.4 (buyStrategies) var buyStrats = pair.buyStrategies != null ? pair.buyStrategies : pair.buyStrategiesData.data; if (buyStrats != null && processBuyStrategies) @@ -470,7 +549,7 @@ namespace Core.Main.DataObjects buyStrategy.Decimals = bs.decimals; buyStrategy.IsTrailing = bs.trailing; buyStrategy.IsTrue = bs.strategyResult; - + dcaLogData.BuyStrategies.Add(buyStrategy); } } @@ -492,9 +571,9 @@ namespace Core.Main.DataObjects sellStrategy.Decimals = ss.decimals; sellStrategy.IsTrailing = ss.trailing; sellStrategy.IsTrue = ss.strategyResult; - + dcaLogData.SellStrategies.Add(sellStrategy); - + // Find the target percentage gain to sell. if (sellStrategy.Name.Contains("GAIN", StringComparison.InvariantCultureIgnoreCase)) { @@ -506,14 +585,14 @@ namespace Core.Main.DataObjects } } } - + // Calculate current value dcaLogData.CurrentValue = dcaLogData.CurrentPrice * dcaLogData.Amount; - + // Convert Unix Timestamp to Datetime System.DateTime rdldDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc); rdldDateTime = rdldDateTime.AddSeconds((double)pair.firstBoughtDate).ToUniversalTime(); - + // Profit Trailer bought times are saved in UTC if (pair.firstBoughtDate > 0) { @@ -528,7 +607,7 @@ namespace Core.Main.DataObjects { dcaLogData.FirstBoughtDate = Constants.confMinDate; } - + _dcaLog.Add(dcaLogData); } @@ -545,7 +624,7 @@ namespace Core.Main.DataObjects buyLogData.CurrentPrice = rbld.currentPrice; buyLogData.PercChange = rbld.percChange; buyLogData.Volume24h = rbld.volume; - + if (rbld.positive != null) { buyLogData.IsTrailing = ((string)(rbld.positive)).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1; @@ -554,10 +633,10 @@ namespace Core.Main.DataObjects else { // Parse buy strategies - + // See if they are using PT 2.5 (buyStrategiesData) or 2.4 (buyStrategies) var buyStrats = rbld.buyStrategies != null ? rbld.buyStrategies : rbld.buyStrategiesData.data; - + if (buyStrats != null) { foreach (var bs in buyStrats) @@ -573,17 +652,17 @@ namespace Core.Main.DataObjects buyStrategy.Decimals = bs.decimals; buyStrategy.IsTrailing = bs.trailing; buyStrategy.IsTrue = bs.strategyResult; - + // Is SOM? buyLogData.IsSom = buyLogData.IsSom || buyStrategy.Name.Contains("som enabled", StringComparison.OrdinalIgnoreCase); - + // Is the pair trailing? buyLogData.IsTrailing = buyLogData.IsTrailing || buyStrategy.IsTrailing; buyLogData.IsTrue = buyLogData.IsTrue || buyStrategy.IsTrue; - + // True status strategy count total buyLogData.TrueStrategyCount += buyStrategy.IsTrue ? 1 : 0; - + // Add buyLogData.BuyStrategies.Add(buyStrategy); } diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index 9c6e1b3..cd89412 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -872,7 +872,7 @@ namespace Core.Main this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version); // Get latest main fiat currency exchange rate - this.GetMainFiatCurrencyDetails(); + // this.GetMainFiatCurrencyDetails(); // Load current PT files this.LoadCurrentProfitTrailerProperties(); @@ -1127,10 +1127,10 @@ namespace Core.Main } } - private void GetMainFiatCurrencyDetails() - { - this.LastRuntimeSummary.MainFiatCurrency = this.LastMainFiatCurrency; - this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = this.LastMainFiatCurrencyExchangeRate; + //private void GetMainFiatCurrencyDetails() + //{ + //this.LastRuntimeSummary.MainFiatCurrency = this.PropertiesData.Currency; + //this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = this.LastMainFiatCurrencyExchangeRate; // if (this.LastFiatCurrencyCheck < DateTime.UtcNow.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) // { @@ -1154,7 +1154,7 @@ namespace Core.Main // this.LastMainFiatCurrencyExchangeRate = 1; // } // } - } + //} // Get current PT properties private void LoadCurrentProfitTrailerProperties() diff --git a/Monitor/Pages/Index.cshtml b/Monitor/Pages/Index.cshtml index fe49026..930214b 100644 --- a/Monitor/Pages/Index.cshtml +++ b/Monitor/Pages/Index.cshtml @@ -33,6 +33,11 @@ $("#baglist-refresh-icon").html(''); $("#buylist-refresh-icon").html(''); + // Add the page refresh code here + setTimeout(function(){ + location.reload(); + }, 60 * 60 * 1000); // 60 minutes + // Clear exisitng interval to stop multiple attempts to load at the same time. if (intervalDashboardTop != null) { @@ -64,13 +69,13 @@ var loadDashboardBottom = function () { //destroy all d3 svg graph to avoid memory leak - $(".nvtooltip").remove(); - $("svg > *").remove(); - $("svg").remove(); - nv.charts = {}; - nv.graphs = []; - nv.logs = {}; - nv.tooltip = {}; + //$(".nvtooltip").remove(); + //$("svg > *").remove(); + //$("svg").remove(); + //nv.charts = {}; + //nv.graphs = []; + //nv.logs = {}; + //nv.tooltip = {}; // Clear exisitng interval to stop multiple attempts to load at the same time. if (intervalDashboardBottom != null) diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index 28240e1..6b646ce 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -26,7 +26,7 @@ } } Total Current Value:   @totalCurrentValueString @Model.Summary.MainMarket - Starting Value:   @Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance   @Model.Summary.MainMarket + Starting Value:   @Model.SummaryData.StartBalance   @Model.Summary.MainMarket @@ -64,7 +64,7 @@ double totalProfit = 0; totalProfit = Model.PTData.SellLog.Sum(s => s.Profit); double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2); - double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2); + double percentGain = Math.Round(totalProfit / Model.SummaryData.StartBalance * 100, 2); double avgDailyGain = Model.DailyGains.Values.Average(dg => dg); double avgMonthlyGain = Model.MonthlyGains.Values.Average(dg => dg); string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; diff --git a/Monitor/Pages/SalesAnalyzer.cshtml.cs b/Monitor/Pages/SalesAnalyzer.cshtml.cs index 74d1b73..049fdb2 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml.cs +++ b/Monitor/Pages/SalesAnalyzer.cshtml.cs @@ -12,6 +12,7 @@ namespace Monitor.Pages public class SalesAnalyzer : _Internal.BasePageModelSecure { public ProfitTrailerData PTData = null; + public SummaryData SummaryData { get; set; } public string TradesChartDataJSON = ""; public string ProfitChartDataJSON = ""; public string BalanceChartDataJSON = ""; @@ -31,6 +32,7 @@ namespace Monitor.Pages private void BindData() { PTData = this.PtDataObject; + SummaryData = this.PTData.Summary; // Convert local offset time to UTC TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); diff --git a/Monitor/Pages/SettingsGeneral.cshtml b/Monitor/Pages/SettingsGeneral.cshtml index 82b7166..1c9f2e8 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml +++ b/Monitor/Pages/SettingsGeneral.cshtml @@ -131,12 +131,12 @@
*@ -
+ @*
- +
-
+
*@
diff --git a/Monitor/Pages/SettingsGeneral.cshtml.cs b/Monitor/Pages/SettingsGeneral.cshtml.cs index 39f6035..57884f2 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml.cs +++ b/Monitor/Pages/SettingsGeneral.cshtml.cs @@ -1,16 +1,19 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.AspNetCore.Http; using Core.Main; using Core.Helper; +using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; + namespace Monitor.Pages { public class SettingsGeneralModel : _Internal.BasePageModelSecure { - public string ValidationMessage = ""; + public string ValidationMessage = ""; + public ProfitTrailerData PTData = null; + public SummaryData SummaryData { get; set; } private string GetTimezoneOffsetString(TimeZoneInfo tzi) { @@ -51,6 +54,8 @@ namespace Monitor.Pages public void OnGet() { base.Init(); + PTData = this.PtDataObject; + SummaryData = this.PTData.Summary; string notification = GetStringParameter("n", ""); if (notification.Equals("BackupRestored")) @@ -68,7 +73,7 @@ namespace Monitor.Pages // Read the new settings PTMagicConfiguration.GeneralSettings.Application.IsEnabled = HttpContext.Request.Form["Application_IsEnabled"].Equals("on"); PTMagicConfiguration.GeneralSettings.Application.TestMode = HttpContext.Request.Form["Application_TestMode"].Equals("on"); - PTMagicConfiguration.GeneralSettings.Application.StartBalance = SystemHelper.TextToDouble(HttpContext.Request.Form["Application_StartBalance"], PTMagicConfiguration.GeneralSettings.Application.StartBalance, "en-US"); + //PTMagicConfiguration.GeneralSettings.Application.StartBalance = SystemHelper.TextToDouble(HttpContext.Request.Form["Application_StartBalance"], PTMagicConfiguration.GeneralSettings.Application.StartBalance, "en-US"); PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName = HttpContext.Request.Form["Application_ProfitTrailerDefaultSettingName"]; PTMagicConfiguration.GeneralSettings.Application.Exchange = HttpContext.Request.Form["Application_Exchange"]; PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL = HttpContext.Request.Form["Application_ProfitTrailerMonitorURL"]; diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index d15c6d4..5ca12c3 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -24,7 +24,7 @@
-
+ @*
*@ @{ string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); if (Model.totalCurrentValue > 100) { @@ -34,8 +34,8 @@
- Start:   @Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance @Model.Summary.MainMarket -     Gain: @Math.Round(((Model.totalCurrentValue - Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance) / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance) * 100, 2)% + Start:   @Model.SummaryData.StartBalance @Model.Summary.MainMarket +     Gain: @Math.Round(((Model.totalCurrentValue - Model.SummaryData.StartBalance) / Model.SummaryData.StartBalance) * 100, 2)%
@@ -49,7 +49,7 @@
-
+ @*
*@
@if (!Model.ProfitChartDataJSON.Equals("")) {
@@ -61,6 +61,7 @@
+
@@ -115,53 +116,58 @@
-
-
+ @*
+
*@

Sales Overviewmore

@{ - var overviewStats = Model.StatsData.FirstOrDefault(); // todaysStats is a new variable + var overviewStats = Model.StatsData; // todaysStats is a new variable var todaysSales = overviewStats.SalesToday; var todaysProfit = overviewStats.ProfitToday; var todaysFunding = overviewStats.FundingToday; var todaysPercentGain = overviewStats.ProfitPercToday; - var todaysFundingGain = todaysPercentGain * ((todaysProfit - todaysFunding) / todaysProfit); + //var todaysFundingGain = todaysPercentGain * ((todaysProfit - todaysFunding) / todaysProfit); - var yesterdaysSales = overviewStats.SalesYesterday; var yesterdaysProfit = overviewStats.ProfitYesterday; var yesterdaysFunding = overviewStats.FundingYesterday; var yesterdaysPercentGain = overviewStats.ProfitPercYesterday; - var yesterdaysFundingGain = yesterdaysPercentGain * ((yesterdaysProfit + yesterdaysFunding) / yesterdaysProfit); + //var yesterdaysFundingGain = yesterdaysPercentGain * ((yesterdaysProfit + yesterdaysFunding) / yesterdaysProfit); var last7DaysSales = overviewStats.SalesWeek; var last7DaysProfit = overviewStats.ProfitWeek; var last7DaysFunding = overviewStats.FundingWeek; var last7DaysPercentGain = overviewStats.ProfitPercWeek; - var last7DaysFundingGain = last7DaysPercentGain * ((last7DaysProfit + last7DaysFunding) / last7DaysProfit); + //var last7DaysFundingGain = last7DaysPercentGain * ((last7DaysProfit + last7DaysFunding) / last7DaysProfit); - var last30DaysSales = overviewStats.SalesMonth; - var last30DaysProfit = overviewStats.ProfitMonth; - var last30DaysFunding = overviewStats.FundingMonth; - var last30DaysPercentGain = overviewStats.ProfitPercMonth; - var last30DaysFundingGain = last30DaysPercentGain * ((last30DaysProfit + last30DaysFunding) / last30DaysProfit); + var thisMonthSales = overviewStats.SalesThisMonth; + var thisMonthProfit = overviewStats.ProfitThisMonth; + var thisMonthFunding = overviewStats.FundingThisMonth; + var thisMonthPercentGain = overviewStats.ProfitPercThisMonth; + //var thisMonthFundingGain = thisMonthPercentGain * ((thisMonthProfit + thisMonthFunding) / thisMonthProfit); + + var lastMonthSales = overviewStats.SalesLastMonth; + var lastMonthProfit = overviewStats.ProfitLastMonth; + var lastMonthFunding = overviewStats.FundingLastMonth; + var lastMonthPercentGain = overviewStats.ProfitPercLastMonth; + //var lastMonthFundingGain = lastMonthPercentGain * ((lastMonthProfit + lastMonthFunding) / lastMonthProfit); var totalSales = overviewStats.TotalSales; var totalProfit = overviewStats.TotalProfit; var totalFunding = overviewStats.FundingTotal; var totalProfitPercent = overviewStats.TotalProfitPerc; - var totalFundingGain = totalProfitPercent * ((totalProfit + totalFunding) / totalProfit); - - double todaysProfitFiat = Math.Round((todaysProfit + todaysFunding) * Model.Summary.MainMarketPrice, 2); - double yesterdaysProfitFiat = Math.Round((yesterdaysProfit + yesterdaysFunding) * Model.Summary.MainMarketPrice, 2); - double last7DaysProfitFiat = Math.Round((last7DaysProfit + last7DaysFunding) * Model.Summary.MainMarketPrice, 2); - double last30DaysProfitFiat = Math.Round((last30DaysProfit + last30DaysFunding) * Model.Summary.MainMarketPrice, 2); - double totalProfitFiat = Math.Round((totalProfit + totalFunding) * Model.Summary.MainMarketPrice, 2); - + //var totalFundingGain = totalProfitPercent * ((totalProfit + totalFunding) / totalProfit); + double todaysProfitFiat = Math.Round((todaysProfit + todaysFunding) * Model.PTData.Summary.FiatConversionRate, 2); + double yesterdaysProfitFiat = Math.Round((yesterdaysProfit + yesterdaysFunding) * Model.PTData.Summary.FiatConversionRate, 2); + double last7DaysProfitFiat = Math.Round((last7DaysProfit + last7DaysFunding) * Model.PTData.Summary.FiatConversionRate, 2); + double thisMonthProfitFiat = Math.Round((thisMonthProfit + thisMonthFunding) * Model.PTData.Summary.FiatConversionRate, 2); + double lastMonthProfitFiat = Math.Round((lastMonthProfit + lastMonthFunding) * Model.PTData.Summary.FiatConversionRate, 2); + double totalProfitFiat = Math.Round((totalProfit + totalFunding) * Model.PTData.Summary.FiatConversionRate, 2); + bool futuresFunding = Model.PropertiesData.IsLeverageExchange; } @@ -169,10 +175,13 @@ - - - + + @if (futuresFunding) + { + + } + @@ -180,41 +189,67 @@ - + + @if (futuresFunding) + { + + } - - + + @if (futuresFunding) + { + + } - - + + @if (futuresFunding) + { + + } - - - - - - - + + + + + @if (futuresFunding) + { + + } + + + + + + + + @if (futuresFunding) + { + + } + - + + @if (futuresFunding) + { + + } -
SalesProfit @Model.Summary.MainMarketFunding@Model.Summary.MainFiatCurrencyProfit @Model.PTData.Summary.Market GainFunding@Model.PTData.Properties.Currency
Today @overviewStats.SalesToday @todaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@todaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@todaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@todaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(todaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@todaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Yesterday @yesterdaysSales @yesterdaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@yesterdaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@yesterdaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@yesterdaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(yesterdaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@yesterdaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Last 7 Days @last7DaysSales @last7DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@last7DaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@last7DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@last7DaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(last7DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@last7DaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Last 30 Days@last30DaysSales@last30DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@last30DaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(last30DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@last30DaysFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%This Month@thisMonthSales@thisMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@thisMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@thisMonthFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(thisMonthProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))
Last Month@lastMonthSales@lastMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@lastMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@lastMonthFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(lastMonthProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))
Total @totalSales @totalProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@totalFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@totalProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@totalFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@totalFundingGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
@@ -226,74 +261,101 @@ + + + + diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index 3f5dbea..890730d 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -6,14 +6,19 @@ using Core.Main; using Core.Helper; using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; -using Core.MarketAnalyzer; +using System.Globalization; +using System.Text; namespace Monitor.Pages { public class DashboardBottomModel : _Internal.BasePageModelSecureAJAX { public ProfitTrailerData PTData = null; - public List StatsData { get; set; } + public StatsData StatsData { get; set; } + public PropertiesData PropertiesData { get; set; } + public SummaryData SummaryData { get; set; } + public List DailyStats { get; set; } = new List(); + public List DailyPNL { get; set; } = new List(); public List MarketTrends { get; set; } = new List(); public string TrendChartDataJSON = ""; public string ProfitChartDataJSON = ""; @@ -35,6 +40,10 @@ namespace Monitor.Pages { PTData = this.PtDataObject; StatsData = this.PTData.Stats; + PropertiesData = this.PTData.Properties; + SummaryData = this.PTData.Summary; + List dailyStatsData = this.PTData.DailyStats; + List dailyPNLData = this.PTData.DailyPNL; // Cleanup temp files FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5); @@ -57,110 +66,101 @@ namespace Monitor.Pages BuildProfitChartData(); } private void BuildMarketTrendChartData() +{ + StringBuilder trendChartDataJSON = new StringBuilder(); + if (MarketTrends.Count > 0) { - if (MarketTrends.Count > 0) - { - TrendChartDataJSON = "["; + trendChartDataJSON.Append("["); int mtIndex = 0; foreach (MarketTrend mt in MarketTrends) { - if (mt.DisplayGraph) - { - string lineColor = ""; - if (mtIndex < Constants.ChartLineColors.Length) + if (mt.DisplayGraph) { - lineColor = Constants.ChartLineColors[mtIndex]; - } - else - { - lineColor = Constants.ChartLineColors[mtIndex - 20]; - } - - if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) - { - List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; - - if (marketTrendChangeSummaries.Count > 0) - { - if (!TrendChartDataJSON.Equals("[")) TrendChartDataJSON += ","; - - TrendChartDataJSON += "{"; - TrendChartDataJSON += "key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',"; - TrendChartDataJSON += "color: '" + lineColor + "',"; - TrendChartDataJSON += "values: ["; - - // Get trend ticks for chart - DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); - DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); - DateTime endDateTime = currentDateTime; - int trendChartTicks = 0; - for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + string lineColor = ""; + if (mtIndex < Constants.ChartLineColors.Length) { - List tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); - if (tickRange.Count > 0) - { - MarketTrendChange mtc = tickRange.First(); - if (tickTime != startDateTime) TrendChartDataJSON += ",\n"; - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; - - TrendChartDataJSON += "{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; - trendChartTicks++; - } + lineColor = Constants.ChartLineColors[mtIndex]; } - // Add most recent tick - List latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); - if (latestTickRange.Count > 0) + else { - MarketTrendChange mtc = latestTickRange.First(); - if (trendChartTicks > 0) TrendChartDataJSON += ",\n"; - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; - TrendChartDataJSON += "{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; + lineColor = Constants.ChartLineColors[mtIndex - 20]; + } + + if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) + { + List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; + + if (marketTrendChangeSummaries.Count > 0) + { + if (!trendChartDataJSON.ToString().Equals("[")) trendChartDataJSON.Append(","); + + trendChartDataJSON.Append("{"); + trendChartDataJSON.Append("key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',"); + trendChartDataJSON.Append("color: '" + lineColor + "',"); + trendChartDataJSON.Append("values: ["); + + // Get trend ticks for chart + DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); + DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); + DateTime endDateTime = currentDateTime; + int trendChartTicks = 0; + for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + { + List tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); + if (tickRange.Count > 0) + { + MarketTrendChange mtc = tickRange.First(); + if (tickTime != startDateTime) trendChartDataJSON.Append(",\n"); + if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; + + trendChartDataJSON.Append("{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); + trendChartTicks++; + } + } + // Add most recent tick + List latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); + if (latestTickRange.Count > 0) + { + MarketTrendChange mtc = latestTickRange.First(); + if (trendChartTicks > 0) trendChartDataJSON.Append(",\n"); + if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; + trendChartDataJSON.Append("{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); + } + trendChartDataJSON.Append("]"); + trendChartDataJSON.Append("}"); + mtIndex++; + } } - TrendChartDataJSON += "]"; - TrendChartDataJSON += "}"; - mtIndex++; - } } - } } - TrendChartDataJSON += "]"; - } + trendChartDataJSON.Append("]"); } + TrendChartDataJSON = trendChartDataJSON.ToString(); +} private void BuildProfitChartData() { - int tradeDayIndex = 0; - string profitPerDayJSON = ""; - - if (PTData.SellLog.Count > 0) - { - DateTime minSellLogDate = PTData.SellLog.OrderBy(sl => sl.SoldDate).First().SoldDate.Date; - DateTime graphStartDate = DateTime.UtcNow.Date.AddDays(-30); - if (minSellLogDate > graphStartDate) + StringBuilder profitPerDayJSON = new StringBuilder(); + + if (PTData.DailyStats.Count > 0) { - graphStartDate = minSellLogDate; + DateTime endDate = DateTime.UtcNow.Date; + DateTime startDate = endDate.AddDays(-30); + + for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) + { + if (profitPerDayJSON.Length > 0) + { + profitPerDayJSON.Append(",\n"); + } + DailyStatsData dailyStats = PTData.DailyStats.Find(ds => DateTime.ParseExact(ds.Date, "d-M-yyyy", CultureInfo.InvariantCulture) == date); + double profitFiat = dailyStats != null ? Math.Round(dailyStats.TotalProfitCurrency,2) : 0; + profitPerDayJSON.Append("{x: new Date('" + date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); + } + ProfitChartDataJSON = "[{key: 'Profit in " + PTData.Properties.Currency + "',color: '" + Constants.ChartLineColors[1] + "',values: [" + profitPerDayJSON.ToString() + "]}]"; } - for (DateTime salesDate = graphStartDate; salesDate <= DateTime.UtcNow.Date; salesDate = salesDate.AddDays(1)) - { - if (tradeDayIndex > 0) - { - profitPerDayJSON += ",\n"; - } - int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Count; - double profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Sum(t => t.Profit); - double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2); - profitPerDayJSON += "{x: new Date('" + salesDate.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; - tradeDayIndex++; - } - ProfitChartDataJSON = "["; - ProfitChartDataJSON += "{"; - ProfitChartDataJSON += "key: 'Profit in " + Summary.MainFiatCurrency + "',"; - ProfitChartDataJSON += "color: '" + Constants.ChartLineColors[1] + "',"; - ProfitChartDataJSON += "values: [" + profitPerDayJSON + "]"; - ProfitChartDataJSON += "}"; - ProfitChartDataJSON += "]"; - } } + private void BuildAssetDistributionData() { // the per PT-Eelroy, the PT API doesn't provide these values when using leverage, so they are calculated here to cover either case. @@ -171,9 +171,9 @@ namespace Monitor.Pages bool isSellStrategyTrue = false; bool isTrailingSellActive = false; - foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog) - { - string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); + foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog) + { + string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); // Aggregate totals double leverage = dcaLogEntry.Leverage; diff --git a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json index beb9ffe..47fc107 100644 --- a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json +++ b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json @@ -10,7 +10,6 @@ "ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list) "ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings) "Exchange": "Bittrex", // The exchange your are running Profit Trailer on - "StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer "TimezoneOffset": "+0:00", // Your timezone offset from UTC time //"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute diff --git a/_Development/DevSettings/settings.general.json b/_Development/DevSettings/settings.general.json index beb9ffe..47fc107 100644 --- a/_Development/DevSettings/settings.general.json +++ b/_Development/DevSettings/settings.general.json @@ -10,7 +10,6 @@ "ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list) "ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings) "Exchange": "Bittrex", // The exchange your are running Profit Trailer on - "StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer "TimezoneOffset": "+0:00", // Your timezone offset from UTC time //"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute From 16fada144368ecd0cf4f146b00432e5c36468a1b Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Thu, 11 Jan 2024 00:58:09 +0900 Subject: [PATCH 04/14] More Dashboard Bottom Optimizations --- Core/DataObjects/PTMagicData.cs | 13 +- Core/DataObjects/ProfitTrailerData.cs | 181 +++++++++++++----- Core/Main/PTMagic.cs | 20 +- Monitor/Pages/Login.cshtml.cs | 2 +- Monitor/Pages/SalesAnalyzer.cshtml | 2 +- Monitor/Pages/SettingsGeneral.cshtml | 9 +- Monitor/Pages/SettingsGeneral.cshtml.cs | 1 + Monitor/Pages/_get/DashboardBottom.cshtml | 52 ++--- Monitor/Pages/_get/DashboardBottom.cshtml.cs | 140 +++++++------- .../settings.general.json | 5 +- .../DevSettings/settings.general.json | 5 +- _Development/Update Notes Template.txt | 2 +- 12 files changed, 263 insertions(+), 169 deletions(-) diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index 298c2bf..a3c5cc7 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -41,10 +41,8 @@ namespace Core.Main.DataObjects.PTMagicData public string ProfitTrailerDefaultSettingName { get; set; } = "default"; public int FloodProtectionMinutes { get; set; } = 15; public string Exchange { get; set; } - //public double StartBalance { get; set; } = 0; public string InstanceName { get; set; } = "PT Magic"; public string TimezoneOffset { get; set; } = "+0:00"; - public string MainFiatCurrency { get; set; } = "USD"; public string CoinMarketCapAPIKey { get; set; } //public string FreeCurrencyConverterAPIKey { get; set; } } @@ -59,6 +57,7 @@ namespace Core.Main.DataObjects.PTMagicData public string AnalyzerChart { get; set; } = ""; public int GraphIntervalMinutes { get; set; } = 60; public int GraphMaxTimeframeHours { get; set; } = 24; + public int ProfitsMaxTimeframeDays { get; set; } = 60; public int RefreshSeconds { get; set; } = 30; public int BagAnalyzerRefreshSeconds { get; set; } = 5; public int BuyAnalyzerRefreshSeconds { get; set; } = 5; @@ -127,6 +126,7 @@ namespace Core.Main.DataObjects.PTMagicData [DefaultValue("Market")] public string TrendCurrency { get; set; } = "Market"; + public string SplitCamelCaseName { get; set; } [DefaultValue(0)] public int MaxMarkets { get; set; } = 0; @@ -442,15 +442,6 @@ namespace Core.Main.DataObjects.PTMagicData public double FundingTotal { get; set; } } - public class DailyStatsData - { - public string Date { get; set; } - public double TotalSales { get; set; } - public double TotalBuys { get; set; } - public double TotalProfitCurrency { get; set; } - public int Order { get; set; } - } - public class DailyPNLData { public string Date { get; set; } diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 465a205..865333e 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net; using System.Threading.Tasks; using System.Diagnostics; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Core.Main.DataObjects.PTMagicData; @@ -17,15 +18,25 @@ namespace Core.Main.DataObjects private SummaryData _summary = null; private PropertiesData _properties = null; private StatsData _stats = null; - private List _dailyStats = new List(); private List _dailyPNL = new List(); + private decimal? _totalProfit = null; + public decimal? TotalProfit + { + get { return _totalProfit; } + set { _totalProfit = value; } + } + private decimal? _totalSales = null; + public decimal? TotalSales + { + get { return _totalSales; } + set { _totalSales = value; } + } private List _sellLog = new List(); private List _dcaLog = new List(); private List _buyLog = new List(); private string _ptmBasePath = ""; private PTMagicConfiguration _systemConfiguration = null; private TransactionData _transactionData = null; - private DateTime _dailyStatsRefresh = DateTime.UtcNow; private DateTime _dailyPNLRefresh = DateTime.UtcNow; private DateTime _statsRefresh = DateTime.UtcNow; private DateTime _buyLogRefresh = DateTime.UtcNow; @@ -143,6 +154,28 @@ namespace Core.Main.DataObjects BaseUrl = PTProperties.baseUrl }; } + // public StatsData Stats + // { + // get + // { + // if (_stats == null || DateTime.UtcNow > _statsRefresh) + // { + // lock (_statsLock) + // { + // if (_stats == null || DateTime.UtcNow > _statsRefresh) + // { + // dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); + // JObject statsDataJObject = statsDataJson as JObject; + // JObject basicSection = (JObject)statsDataJObject["basic"]; + // _stats = BuildStatsData(basicSection); + // _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + // } + // } + // } + // return _stats; + // } + // } + public StatsData Stats { get @@ -153,17 +186,29 @@ namespace Core.Main.DataObjects { if (_stats == null || DateTime.UtcNow > _statsRefresh) { - dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); - JObject statsDataJObject = statsDataJson as JObject; - JObject basicSection = (JObject)statsDataJObject["basic"]; - _stats = BuildStatsData(basicSection); - _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats")) + using (var reader = new StreamReader(stream)) + using (var jsonReader = new JsonTextReader(reader)) + { + while (jsonReader.Read()) + { + if (jsonReader.TokenType == JsonToken.PropertyName && (string)jsonReader.Value == "basic") + { + jsonReader.Read(); // Move to the value of the "basic" property + JObject basicSection = JObject.Load(jsonReader); + _stats = BuildStatsData(basicSection); + _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + break; + } + } + } } } } return _stats; } } + private StatsData BuildStatsData(dynamic statsDataJson) { return new StatsData() @@ -194,38 +239,28 @@ namespace Core.Main.DataObjects FundingTotal = statsDataJson["totalFunding"] }; } - public List DailyStats - { - get - { - if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) - { - lock (_dailyStatsLock) - { - if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) - { - dynamic dailyStatsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); - JObject dailyStatsDataJObject = dailyStatsDataJson as JObject; - JArray dailyStatsSection = (JArray)dailyStatsDataJObject["extra"]["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"], - TotalProfitCurrency = dailyStatsDataJson["totalProfitCurrency"], - Order = dailyStatsDataJson["order"], - }; - } + // public List DailyPNL + // { + // get + // { + // if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + // { + // lock (_dailyPNLLock) + // { + // if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + // { + // dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); + // JObject dailyPNLDataJObject = dailyPNLDataJson as JObject; + // JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"]; + // _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); + // _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + // } + // } + // } + // return _dailyPNL; + // } + // } + public List DailyPNL { get @@ -236,11 +271,52 @@ namespace Core.Main.DataObjects { if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) { - dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); - JObject dailyPNLDataJObject = dailyPNLDataJson as JObject; - JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"]; - _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); - _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + 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 && + ((_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 dailyPNLSection = (JArray)extraSection["dailyPNLStats"]; + _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); + _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } } } } @@ -455,6 +531,23 @@ namespace Core.Main.DataObjects return JArray.Parse(rawBody); } } + private Stream GetDataFromProfitTrailerAsStream(string callPath) + { + string url = string.Format("{0}{1}{2}token={3}", _systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL, + callPath, + callPath.Contains("?") ? "&" : "?", + _systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken); + + // Get the data from PT + Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url)); + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.AutomaticDecompression = DecompressionMethods.GZip; + request.KeepAlive = true; + + WebResponse response = request.GetResponse(); + + return response.GetResponseStream(); + } private void BuildSellLogData(dynamic rawSellLogData) diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index cd89412..c42f770 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -14,12 +14,11 @@ namespace Core.Main { public class PTMagic { - public PTMagic(LogHelper log) { this.Log = log; } - + #region Properties private LogHelper _log; private PTMagicConfiguration _systemConfiguration; @@ -84,7 +83,10 @@ namespace Core.Main _systemConfiguration = value; } } - + public class IsAnalzyerRunning + { + private string _isAnalyzerRunning; + } public System.Timers.Timer Timer { get @@ -720,15 +722,6 @@ namespace Core.Main this.Log.DoLogInfo("No CoinMarketCap API KEY specified! That's ok, but you can't use CoinMarketCap in your settings.analyzer.json"); } - // Check for CurrencyConverterApi Key - //if (!this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey.Equals("")) - //{ - // this.Log.DoLogInfo("FreeCurrencyConverterApi KEY found"); - //} - //else - //{ - // this.Log.DoLogInfo("No FreeCurrencyConverterApi KEY specified. That's ok! But you can only use USD; apply for a key at: https://freecurrencyrates.com/en"); - //} } catch (System.NullReferenceException) { @@ -871,9 +864,6 @@ namespace Core.Main // Check for latest GitHub version this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version); - // Get latest main fiat currency exchange rate - // this.GetMainFiatCurrencyDetails(); - // Load current PT files this.LoadCurrentProfitTrailerProperties(); diff --git a/Monitor/Pages/Login.cshtml.cs b/Monitor/Pages/Login.cshtml.cs index 44c35c5..a78502c 100644 --- a/Monitor/Pages/Login.cshtml.cs +++ b/Monitor/Pages/Login.cshtml.cs @@ -31,7 +31,7 @@ namespace Monitor.Pages { HttpContext.Session.SetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(), DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")); PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = true; - PTMagicConfiguration.WriteGeneralSettings(); + //PTMagicConfiguration.WriteGeneralSettings(); if (cbRememberMe != null) { if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase)) diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index 6b646ce..2311024 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -37,7 +37,7 @@
-

Cumulative Profits

+

Cumulative PNL

diff --git a/Monitor/Pages/SettingsGeneral.cshtml b/Monitor/Pages/SettingsGeneral.cshtml index 1c9f2e8..d460948 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml +++ b/Monitor/Pages/SettingsGeneral.cshtml @@ -203,12 +203,19 @@
- +
+
+ +
+ +
+
+
diff --git a/Monitor/Pages/SettingsGeneral.cshtml.cs b/Monitor/Pages/SettingsGeneral.cshtml.cs index 57884f2..a41c1cb 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml.cs +++ b/Monitor/Pages/SettingsGeneral.cshtml.cs @@ -90,6 +90,7 @@ namespace Monitor.Pages PTMagicConfiguration.GeneralSettings.Monitor.Port = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_Port"], PTMagicConfiguration.GeneralSettings.Monitor.Port); PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_GraphIntervalMinutes"], PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes); PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_GraphMaxTimeframeHours"], PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); + PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_ProfitsMaxTimeframeDays"], PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays); PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_RefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds); PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BagAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds); PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BuyAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds); diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index 5ca12c3..c56c504 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -81,34 +81,38 @@ @foreach (var marketTrend in Model.MarketTrends.OrderBy(mt => mt.TrendMinutes)) { - if (Model.Summary.MarketTrendChanges.ContainsKey(marketTrend.Name)) { - double trendChange = Model.Summary.MarketTrendChanges[marketTrend.Name].OrderByDescending(mtc => mtc.TrendDateTime).First().TrendChange; - string trendChangeOutput = trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); + if (Model.Summary.MarketTrendChanges.ContainsKey(marketTrend.Name)) { + double trendChange = Model.Summary.MarketTrendChanges[marketTrend.Name].Last().TrendChange; + string trendChangeOutput = trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); - int marketCount = marketTrend.MaxMarkets; - string marketCountString = marketCount.ToString(); + int marketCount = marketTrend.MaxMarkets; + string marketCountString = marketCount.ToString(); - if (marketCount == 0) { - marketCountString = "All"; - } else if (marketCount > Model.Summary.MarketSummary.Keys.Count && marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) { - marketCountString = Model.Summary.MarketSummary.Keys.Count.ToString(); + if (marketCount == 0) { + marketCountString = "All"; + } else if (marketCount > Model.Summary.MarketSummary.Keys.Count && marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) { + marketCountString = Model.Summary.MarketSummary.Keys.Count.ToString(); + } + + // Cache the result of SplitCamelCase(marketTrend.Name) + string splitCamelCaseName = Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name); + + + @splitCamelCaseName + @marketCountString + @Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false) + @if (marketTrend.TrendThreshold == 0) + { + -- + } + else + { + @marketTrend.TrendThreshold + } + @trendChangeOutput% + } - - @Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name) - @marketCountString - @Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false) - @if (marketTrend.TrendThreshold == 0) - { - -- - } - else - { - @marketTrend.TrendThreshold - } - @trendChangeOutput% - } - }
diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index 890730d..b1bd454 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -17,8 +17,7 @@ namespace Monitor.Pages public StatsData StatsData { get; set; } public PropertiesData PropertiesData { get; set; } public SummaryData SummaryData { get; set; } - public List DailyStats { get; set; } = new List(); - public List DailyPNL { get; set; } = new List(); + public List MarketTrends { get; set; } = new List(); public string TrendChartDataJSON = ""; public string ProfitChartDataJSON = ""; @@ -42,7 +41,7 @@ namespace Monitor.Pages StatsData = this.PTData.Stats; PropertiesData = this.PTData.Properties; SummaryData = this.PTData.Summary; - List dailyStatsData = this.PTData.DailyStats; + List dailyPNLData = this.PTData.DailyPNL; // Cleanup temp files @@ -66,96 +65,103 @@ namespace Monitor.Pages BuildProfitChartData(); } private void BuildMarketTrendChartData() -{ - StringBuilder trendChartDataJSON = new StringBuilder(); - if (MarketTrends.Count > 0) { - trendChartDataJSON.Append("["); - int mtIndex = 0; - foreach (MarketTrend mt in MarketTrends) + List trendChartData = new List(); + if (MarketTrends.Count > 0) { - if (mt.DisplayGraph) + + int mtIndex = 0; + foreach (MarketTrend mt in MarketTrends) { - string lineColor = ""; - if (mtIndex < Constants.ChartLineColors.Length) + if (mt.DisplayGraph) { - lineColor = Constants.ChartLineColors[mtIndex]; - } - else - { - lineColor = Constants.ChartLineColors[mtIndex - 20]; - } + string lineColor = mtIndex < Constants.ChartLineColors.Length + ? Constants.ChartLineColors[mtIndex] + : Constants.ChartLineColors[mtIndex - 20]; - if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) - { - List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; - - if (marketTrendChangeSummaries.Count > 0) + if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) { - if (!trendChartDataJSON.ToString().Equals("[")) trendChartDataJSON.Append(","); + List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; - trendChartDataJSON.Append("{"); - trendChartDataJSON.Append("key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',"); - trendChartDataJSON.Append("color: '" + lineColor + "',"); - trendChartDataJSON.Append("values: ["); - - // Get trend ticks for chart - DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); - DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); - DateTime endDateTime = currentDateTime; - int trendChartTicks = 0; - for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + if (marketTrendChangeSummaries.Count > 0) { - List tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); - if (tickRange.Count > 0) + List trendValues = new List(); + + // Sort marketTrendChangeSummaries by TrendDateTime + marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList(); + + // Get trend ticks for chart + DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); + DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); + DateTime endDateTime = currentDateTime; + + // Cache the result of SplitCamelCase(mt.Name) + string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name); + + for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) { - MarketTrendChange mtc = tickRange.First(); - if (tickTime != startDateTime) trendChartDataJSON.Append(",\n"); - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; + // Use binary search to find the range of items that match the condition + int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer.Create((x, y) => x.TrendDateTime.CompareTo(y.TrendDateTime))); + if (index < 0) index = ~index; + if (index < marketTrendChangeSummaries.Count) + { + MarketTrendChange mtc = marketTrendChangeSummaries[index]; + if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; - trendChartDataJSON.Append("{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); - trendChartTicks++; + trendValues.Add("{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + } } + + // Add most recent tick + MarketTrendChange latestMtc = marketTrendChangeSummaries.Last(); + if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0; + trendValues.Add("{ x: new Date('" + latestMtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + + // Use cached splitCamelCaseName + trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }"); + mtIndex++; } - // Add most recent tick - List latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); - if (latestTickRange.Count > 0) - { - MarketTrendChange mtc = latestTickRange.First(); - if (trendChartTicks > 0) trendChartDataJSON.Append(",\n"); - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; - trendChartDataJSON.Append("{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); - } - trendChartDataJSON.Append("]"); - trendChartDataJSON.Append("}"); - mtIndex++; } } } + } - trendChartDataJSON.Append("]"); + TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]"; } - TrendChartDataJSON = trendChartDataJSON.ToString(); -} private void BuildProfitChartData() { StringBuilder profitPerDayJSON = new StringBuilder(); - if (PTData.DailyStats.Count > 0) + if (PTData.DailyPNL.Count > 0) { DateTime endDate = DateTime.UtcNow.Date; - DateTime startDate = endDate.AddDays(-30); - + DateTime startDate = endDate.AddDays(-PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays - 1); // Fetch data for timeframe + 1 days + double previousDayCumulativeProfit = 0; + bool isFirstDay = true; for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) { - if (profitPerDayJSON.Length > 0) + DailyPNLData dailyPNL = PTData.DailyPNL.Find(ds => DateTime.ParseExact(ds.Date, "d-M-yyyy", CultureInfo.InvariantCulture) == date); + if (dailyPNL != null) { - profitPerDayJSON.Append(",\n"); + if (isFirstDay) + { + isFirstDay = false; + } + else + { + // Calculate the profit for the current day + double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2); + + // Add the data point to the JSON string + if (profitPerDayJSON.Length > 0) + { + profitPerDayJSON.Append(",\n"); + } + profitPerDayJSON.Append("{x: new Date('" + date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); + } + previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency; } - DailyStatsData dailyStats = PTData.DailyStats.Find(ds => DateTime.ParseExact(ds.Date, "d-M-yyyy", CultureInfo.InvariantCulture) == date); - double profitFiat = dailyStats != null ? Math.Round(dailyStats.TotalProfitCurrency,2) : 0; - profitPerDayJSON.Append("{x: new Date('" + date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); } ProfitChartDataJSON = "[{key: 'Profit in " + PTData.Properties.Currency + "',color: '" + Constants.ChartLineColors[1] + "',values: [" + profitPerDayJSON.ToString() + "]}]"; } @@ -171,8 +177,8 @@ namespace Monitor.Pages bool isSellStrategyTrue = false; bool isTrailingSellActive = false; - foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog) - { + foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog) + { string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); // Aggregate totals diff --git a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json index 47fc107..50a9d5d 100644 --- a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json +++ b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json @@ -23,8 +23,9 @@ "Port": 8080, // The port you want to run your monitor on "RootUrl": "/", // The root Url of your monitor "AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your default currency against USD. You can change that here. (eg., BTCEUR) - "GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points - "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers + "GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points in minutes + "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers in hours + "ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days "RefreshSeconds": 30, // The refresh interval of your monitor main page "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart. diff --git a/_Development/DevSettings/settings.general.json b/_Development/DevSettings/settings.general.json index 47fc107..50a9d5d 100644 --- a/_Development/DevSettings/settings.general.json +++ b/_Development/DevSettings/settings.general.json @@ -23,8 +23,9 @@ "Port": 8080, // The port you want to run your monitor on "RootUrl": "/", // The root Url of your monitor "AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your default currency against USD. You can change that here. (eg., BTCEUR) - "GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points - "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers + "GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points in minutes + "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers in hours + "ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days "RefreshSeconds": 30, // The refresh interval of your monitor main page "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart. diff --git a/_Development/Update Notes Template.txt b/_Development/Update Notes Template.txt index 6004880..c949b28 100644 --- a/_Development/Update Notes Template.txt +++ b/_Development/Update Notes Template.txt @@ -16,7 +16,7 @@ - This version of PT Magic only works with Profit Trailer v2.4.x and above ### .Net -- PTMagic requires .Net Core Runtime 3.1 on the host operating system (download: https://dotnet.microsoft.com/download) +- PTMagic requires .Net Core Runtime 7 on the host operating system (download: https://dotnet.microsoft.com/download) ### Settings - **If you are updating from any version of PTMagic prior to 2.4.1,** check the default settings included with this release for any new lines that might need to be added to your settings.general.json file. From 08554da6ee7d5a8d053dd2c540f11766328f63a3 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Sun, 14 Jan 2024 04:10:44 +0900 Subject: [PATCH 05/14] More Dashboard bottom optimizations --- Monitor/Pages/Index.cshtml | 22 +- Monitor/Pages/SettingsGeneral.cshtml | 6 +- Monitor/Pages/StatusSummary.cshtml | 108 +++++---- Monitor/Pages/_get/DashboardBottom.cshtml | 94 +++++++- Monitor/Pages/_get/DashboardBottom.cshtml.cs | 225 ++++++++++++------- 5 files changed, 290 insertions(+), 165 deletions(-) diff --git a/Monitor/Pages/Index.cshtml b/Monitor/Pages/Index.cshtml index 930214b..770f037 100644 --- a/Monitor/Pages/Index.cshtml +++ b/Monitor/Pages/Index.cshtml @@ -33,11 +33,6 @@ $("#baglist-refresh-icon").html(''); $("#buylist-refresh-icon").html(''); - // Add the page refresh code here - setTimeout(function(){ - location.reload(); - }, 60 * 60 * 1000); // 60 minutes - // Clear exisitng interval to stop multiple attempts to load at the same time. if (intervalDashboardTop != null) { @@ -69,13 +64,16 @@ var loadDashboardBottom = function () { //destroy all d3 svg graph to avoid memory leak - //$(".nvtooltip").remove(); - //$("svg > *").remove(); - //$("svg").remove(); - //nv.charts = {}; - //nv.graphs = []; - //nv.logs = {}; - //nv.tooltip = {}; + setTimeout(function() + { + $(".nvtooltip").remove(); + $("svg > *").remove(); + $("svg").remove(); + nv.charts = {}; + nv.graphs = []; + nv.logs = {}; + nv.tooltip = {}; + }, 10 * intervalDashboardBottom * 1000); // 10 times intervalDashboardBottom in milliseconds // Clear exisitng interval to stop multiple attempts to load at the same time. if (intervalDashboardBottom != null) diff --git a/Monitor/Pages/SettingsGeneral.cshtml b/Monitor/Pages/SettingsGeneral.cshtml index d460948..3b8434d 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml +++ b/Monitor/Pages/SettingsGeneral.cshtml @@ -196,21 +196,21 @@
- +
- +
- +
diff --git a/Monitor/Pages/StatusSummary.cshtml b/Monitor/Pages/StatusSummary.cshtml index 46eb477..7bab1c6 100644 --- a/Monitor/Pages/StatusSummary.cshtml +++ b/Monitor/Pages/StatusSummary.cshtml @@ -10,7 +10,8 @@ }
-
+ +

PTMagic Status

@{ @@ -71,7 +72,6 @@ @{ string maxCostCaption = "Initial"; } - @@ -99,69 +99,63 @@
- - -
-
-
-
-

Settings Active Time (Last 24h)

- -
- -
-
-
-
-
-

Settings Active Time (Last 3 days)

- -
- -
-
+
+
+
+

Settings Active Time (Last 24h)

+
+
-
-

Global Settings Log

- - - - - - - - - - - - @foreach (Core.Main.DataObjects.PTMagicData.GlobalSettingSummary gss in Model.Summary.GlobalSettingSummary.OrderByDescending(g => g.SwitchDateTime).Take(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxSettingsLogEntries)) { - TimeSpan offsetTimeSpan = TimeSpan.Parse(Model.PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); - DateTimeOffset settingActivationTime = gss.SwitchDateTime; - settingActivationTime = settingActivationTime.ToOffset(offsetTimeSpan); - - string marketTrendsSummary = ""; - foreach (string mt in gss.MarketTrendChanges.Keys) { - if (!marketTrendsSummary.Equals("")) { - marketTrendsSummary += "
"; - } - marketTrendsSummary += Core.Helper.SystemHelper.SplitCamelCase(mt) + ": " + gss.MarketTrendChanges[mt].TrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; - } - - - - - - - } - -
Activation TimeSettingActive TimeMarket Trends
@settingActivationTime.DateTime.ToShortDateString() @settingActivationTime.DateTime.ToShortTimeString()@Core.Helper.SystemHelper.SplitCamelCase(gss.SettingName)@Core.Helper.SystemHelper.GetProperDurationTime(gss.ActiveSeconds)@Html.Raw(marketTrendsSummary)
+

Settings Active Time (Last 3 days)

+
+ +
+ + +
+ + + +
+

Global Settings Log

+ + + + + + + + + + + @foreach (Core.Main.DataObjects.PTMagicData.GlobalSettingSummary gss in Model.Summary.GlobalSettingSummary.OrderByDescending(g => g.SwitchDateTime).Take(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxSettingsLogEntries)) { + TimeSpan offsetTimeSpan = TimeSpan.Parse(Model.PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); + DateTimeOffset settingActivationTime = gss.SwitchDateTime; + settingActivationTime = settingActivationTime.ToOffset(offsetTimeSpan); + + string marketTrendsSummary = ""; + foreach (string mt in gss.MarketTrendChanges.Keys) { + if (!marketTrendsSummary.Equals("")) { + marketTrendsSummary += "
"; + } + marketTrendsSummary += Core.Helper.SystemHelper.SplitCamelCase(mt) + ": " + gss.MarketTrendChanges[mt].TrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; + } + + + + + + + } + +
Activation TimeSettingActive TimeMarket Trends
@settingActivationTime.DateTime.ToShortDateString() @settingActivationTime.DateTime.ToShortTimeString()@Core.Helper.SystemHelper.SplitCamelCase(gss.SettingName)@Core.Helper.SystemHelper.GetProperDurationTime(gss.ActiveSeconds)@Html.Raw(marketTrendsSummary)
diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index c56c504..b4cf026 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -11,8 +11,8 @@
-
-
+
+

Market Trend History

@if (!Model.TrendChartDataJSON.Equals("")) {
@@ -22,9 +22,10 @@ }
+
-
- @*
*@ +
+
@{ string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); if (Model.totalCurrentValue > 100) { @@ -50,7 +51,8 @@
@*
*@ -
+
+

Daily Profit @if (!Model.ProfitChartDataJSON.Equals("")) {
@@ -65,8 +67,8 @@
-
-
+ @*
+
*@

Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange more

@@ -293,6 +295,22 @@ .datum(assetDistributionData) .transition().duration(0) .call(assetDistributionChart); + + // Add mouseleave, and mousemove event listeners to hide tooltip + d3.select('.profit-chart').on('mouseleave', function() { + d3.select('.nvtooltip').style('opacity', 0); + }); + + d3.select('body').on('mousemove', function() { + var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect(); + var mouseX = d3.event.clientX; + var mouseY = d3.event.clientY; + + if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) { + d3.select('.nvtooltip').style('opacity', 0); + } + }); + return assetDistributionChart; }); @@ -325,6 +343,21 @@ .datum(trendData) .transition().duration(0) .call(trendChart); + // Add mouseleave, and mousemove event listeners to hide tooltip + d3.select('.profit-chart').on('mouseleave', function() { + d3.select('.nvtooltip').style('opacity', 0); + }); + + d3.select('body').on('mousemove', function() { + var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect(); + var mouseX = d3.event.clientX; + var mouseY = d3.event.clientY; + + if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) { + d3.select('.nvtooltip').style('opacity', 0); + } + }); + return trendChart; }); @@ -348,7 +381,7 @@ profitChart = nv.models.lineChart(); var height = 300; profitChart.useInteractiveGuideline(true); - profitChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); }); + profitChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); }); profitChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f')); profitData = @Html.Raw(Model.ProfitChartDataJSON); @@ -357,9 +390,54 @@ .datum(profitData) .transition().duration(0) .call(profitChart); + + // Add mouseleave, and mousemove event listeners to hide tooltip + d3.select('.profit-chart').on('mouseleave', function() { + d3.select('.nvtooltip').style('opacity', 0); + }); + + d3.select('body').on('mousemove', function() { + var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect(); + var mouseX = d3.event.clientX; + var mouseY = d3.event.clientY; + + if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) { + d3.select('.nvtooltip').style('opacity', 0); + } + }); + + return profitChart; }); } })(jQuery); + + diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index b1bd454..3220282 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -8,6 +8,7 @@ using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; using System.Globalization; using System.Text; +using System.Diagnostics; namespace Monitor.Pages { @@ -19,6 +20,8 @@ namespace Monitor.Pages public SummaryData SummaryData { get; set; } public List MarketTrends { get; set; } = new List(); + public double DataHours { get; set; } + public int ProfitDays { get; set; } public string TrendChartDataJSON = ""; public string ProfitChartDataJSON = ""; public string LastGlobalSetting = "Default"; @@ -31,10 +34,9 @@ namespace Monitor.Pages base.Init(); BindData(); - - BuildAssetDistributionData(); + } - + private void BindData() { PTData = this.PtDataObject; @@ -62,110 +64,164 @@ namespace Monitor.Pages MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList(); BuildMarketTrendChartData(); + BuildAssetDistributionData(); BuildProfitChartData(); } private void BuildMarketTrendChartData() +{ + List trendChartData = new List(); + if (MarketTrends.Count > 0) { - List trendChartData = new List(); - if (MarketTrends.Count > 0) + + int mtIndex = 0; + foreach (MarketTrend mt in MarketTrends) { - - int mtIndex = 0; - foreach (MarketTrend mt in MarketTrends) + if (mt.DisplayGraph) { - if (mt.DisplayGraph) + string lineColor = mtIndex < Constants.ChartLineColors.Length + ? Constants.ChartLineColors[mtIndex] + : Constants.ChartLineColors[mtIndex - 20]; + + if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) { - string lineColor = mtIndex < Constants.ChartLineColors.Length - ? Constants.ChartLineColors[mtIndex] - : Constants.ChartLineColors[mtIndex - 20]; + List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; - if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) + if (marketTrendChangeSummaries.Count > 0) { - List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; + List trendValues = new List(); - if (marketTrendChangeSummaries.Count > 0) + // Sort marketTrendChangeSummaries by TrendDateTime + marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList(); + + // Get trend ticks for chart + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); + + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) { - List trendValues = new List(); - - // Sort marketTrendChangeSummaries by TrendDateTime - marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList(); - - // Get trend ticks for chart - DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); - DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); - DateTime endDateTime = currentDateTime; - - // Cache the result of SplitCamelCase(mt.Name) - string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name); - - for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) - { - // Use binary search to find the range of items that match the condition - int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer.Create((x, y) => x.TrendDateTime.CompareTo(y.TrendDateTime))); - if (index < 0) index = ~index; - if (index < marketTrendChangeSummaries.Count) - { - MarketTrendChange mtc = marketTrendChangeSummaries[index]; - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; - - trendValues.Add("{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); - } - } - - // Add most recent tick - MarketTrendChange latestMtc = marketTrendChangeSummaries.Last(); - if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0; - trendValues.Add("{ x: new Date('" + latestMtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); - - // Use cached splitCamelCaseName - trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }"); - mtIndex++; + offset = TimeSpan.Zero; // If offset is invalid, set it to zero } + + DateTime currentDateTime = DateTime.UtcNow; + DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); + DateTime endDateTime = currentDateTime; + + // Ensure startDateTime doesn't exceed the available data + DateTime earliestTrendDateTime = marketTrendChangeSummaries.Min(mtc => mtc.TrendDateTime); + startDateTime = startDateTime > earliestTrendDateTime ? startDateTime : earliestTrendDateTime; + DataHours = (currentDateTime - earliestTrendDateTime).TotalHours; + + // Cache the result of SplitCamelCase(mt.Name) + string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name); + + for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + { + // Use binary search to find the range of items that match the condition + int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer.Create((x, y) => x.TrendDateTime.CompareTo(y.TrendDateTime))); + if (index < 0) index = ~index; + if (index < marketTrendChangeSummaries.Count) + { + MarketTrendChange mtc = marketTrendChangeSummaries[index]; + if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; + + // Adjust tickTime to the desired timezone before converting to string + DateTime adjustedTickTime = tickTime.Add(isNegative ? -offset : offset); + trendValues.Add("{ x: new Date('" + adjustedTickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + } + } + + // Add most recent tick + MarketTrendChange latestMtc = marketTrendChangeSummaries.Last(); + if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0; + + // Adjust latestMtc.TrendDateTime to the desired timezone before converting to string + DateTime adjustedLatestTrendDateTime = latestMtc.TrendDateTime.Add(isNegative ? -offset : offset); + trendValues.Add("{ x: new Date('" + adjustedLatestTrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + + // Use cached splitCamelCaseName + trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }"); + mtIndex++; } } } - } - TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]"; } + TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]"; +} private void BuildProfitChartData() +{ + List profitPerDayList = new List(); + + if (PTData.DailyPNL.Count > 0) { - StringBuilder profitPerDayJSON = new StringBuilder(); + // Get timezone offset + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); - if (PTData.DailyPNL.Count > 0) + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) { - DateTime endDate = DateTime.UtcNow.Date; - DateTime startDate = endDate.AddDays(-PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays - 1); // Fetch data for timeframe + 1 days - double previousDayCumulativeProfit = 0; - bool isFirstDay = true; - for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) - { - DailyPNLData dailyPNL = PTData.DailyPNL.Find(ds => DateTime.ParseExact(ds.Date, "d-M-yyyy", CultureInfo.InvariantCulture) == date); - if (dailyPNL != null) - { - if (isFirstDay) - { - isFirstDay = false; - } - else - { - // Calculate the profit for the current day - double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2); - - // Add the data point to the JSON string - if (profitPerDayJSON.Length > 0) - { - profitPerDayJSON.Append(",\n"); - } - profitPerDayJSON.Append("{x: new Date('" + date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"); - } - previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency; - } - } - ProfitChartDataJSON = "[{key: 'Profit in " + PTData.Properties.Currency + "',color: '" + Constants.ChartLineColors[1] + "',values: [" + profitPerDayJSON.ToString() + "]}]"; + offset = TimeSpan.Zero; // If offset is invalid, set it to zero } + + DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date; + + // Parse dates once and adjust them to the local timezone + Dictionary dailyPNLByDate = PTData.DailyPNL + .Select(data => { + DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture); + DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset); + return new { Date = dateLocal.Date, Data = data }; + }) + .ToDictionary( + item => item.Date, + item => item.Data + ); + + DateTime earliestDataDate = dailyPNLByDate.Keys.Min(); + DateTime startDate = endDate.AddDays(-PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays - 1); // Fetch data for timeframe + 1 days + if (startDate < earliestDataDate) + { + startDate = earliestDataDate; + } + + // Calculate the total days of data available + ProfitDays = (endDate - startDate).Days; + + double previousDayCumulativeProfit = 0; + bool isFirstDay = true; + for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) + { + // Use the dictionary to find the DailyPNLData for the date + if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL)) + { + if (isFirstDay) + { + isFirstDay = false; + } + else + { + // Calculate the profit for the current day + double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2); + + // Add the data point to the list + profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat }); + } + previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency; + } + } + // Convert the list to a JSON string using Newtonsoft.Json + ProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { + new { + key = "Profit in " + PTData.Properties.Currency, + color = Constants.ChartLineColors[1], + values = profitPerDayList + } + }); } +} private void BuildAssetDistributionData() { @@ -179,8 +235,7 @@ namespace Monitor.Pages foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog) { - string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); - + string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); // Aggregate totals double leverage = dcaLogEntry.Leverage; if (leverage == 0) From ae15f7440ffc3ded525bcec991c6f4b78b5921a5 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Mon, 15 Jan 2024 23:12:46 +0900 Subject: [PATCH 06/14] Summary --- Core/DataObjects/PTMagicData.cs | 20 +- Core/DataObjects/ProfitTrailerData.cs | 209 +++++++++++-------- Core/Main/PTMagic.cs | 29 --- Monitor/Pages/SalesAnalyzer.cshtml | 82 +++++--- Monitor/Pages/SalesAnalyzer.cshtml.cs | 66 +++++- Monitor/Pages/SettingsGeneral.cshtml.cs | 4 +- Monitor/Pages/_get/DashboardBottom.cshtml | 20 +- Monitor/Pages/_get/DashboardBottom.cshtml.cs | 8 +- 8 files changed, 268 insertions(+), 170 deletions(-) diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index a3c5cc7..3e72643 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -314,8 +314,8 @@ namespace Core.Main.DataObjects.PTMagicData public double MainMarketPrice { get; set; } = 0; private PropertiesData _propertiesData = new PropertiesData(); public string MainFiatCurrency => _propertiesData.Currency; - private SummaryData _summaryData = new SummaryData(); - public double MainFiatCurrencyExchangeRate => _summaryData.FiatConversionRate; + private MiscData _miscData = new MiscData(); + public double MainFiatCurrencyExchangeRate => _miscData.FiatConversionRate; public List BuyStrategies { get; set; } = new List(); public List SellStrategies { get; set; } = new List(); public List DCABuyStrategies { get; set; } = new List(); @@ -447,6 +447,15 @@ namespace Core.Main.DataObjects.PTMagicData public string Date { get; set; } public double CumulativeProfitCurrency { get; set; } public double Order { get; set; } + + } + public class MonthlyStatsData + { + public string Month { get; set; } + public double TotalProfitCurrency { get; set; } + public double TotalSales { get; set; } + public double AvgGrowth { get; set; } + } public class PTStrategy @@ -524,8 +533,9 @@ namespace Core.Main.DataObjects.PTMagicData public List BuyStrategies { get; set; } = new List(); } - public class SummaryData - { public double Balance { get; set; } + public class MiscData + { + public double Balance { get; set; } public double StartBalance { get; set; } public double FiatConversionRate { get; set; } public double PairsValue { get; set; } @@ -533,5 +543,7 @@ namespace Core.Main.DataObjects.PTMagicData public double PendingValue { get; set; } public double DustValue { get; set; } public string Market { get; set; } + public string TotalCurrentValue { get; set; } + public string TimeZoneOffset { get; set; } } } \ No newline at end of file diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 865333e..0894193 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -15,10 +15,11 @@ namespace Core.Main.DataObjects public class ProfitTrailerData { - private SummaryData _summary = null; + private MiscData _misc = null; private PropertiesData _properties = null; private StatsData _stats = null; private List _dailyPNL = new List(); + private List _monthlyStats = new List(); private decimal? _totalProfit = null; public decimal? TotalProfit { @@ -38,20 +39,23 @@ namespace Core.Main.DataObjects private PTMagicConfiguration _systemConfiguration = null; private TransactionData _transactionData = null; private DateTime _dailyPNLRefresh = DateTime.UtcNow; + private DateTime _monthlyStatsRefresh = DateTime.UtcNow; private DateTime _statsRefresh = DateTime.UtcNow; private DateTime _buyLogRefresh = DateTime.UtcNow; private DateTime _sellLogRefresh = DateTime.UtcNow; private DateTime _dcaLogRefresh = DateTime.UtcNow; - private DateTime _summaryRefresh = DateTime.UtcNow; + private DateTime _miscRefresh = DateTime.UtcNow; private DateTime _propertiesRefresh = DateTime.UtcNow; private volatile object _dailyStatsLock = new object(); - private volatile object _dailyPNLLock = new object(); + private volatile object _dailyPNLLock = new object(); + private volatile object _monthlyStatsLock = new object(); private volatile object _statsLock = new object(); private volatile object _buyLock = new object(); private volatile object _sellLock = new object(); private volatile object _dcaLock = new object(); - private volatile object _summaryLock = new object(); - private volatile object _propertiesLock = new object(); private TimeSpan? _offsetTimeSpan = null; + private volatile object _miscLock = new object(); + private volatile object _propertiesLock = new object(); + private TimeSpan? _offsetTimeSpan = null; public void DoLog(string message) { // Implement your logging logic here @@ -87,29 +91,29 @@ namespace Core.Main.DataObjects } } - public SummaryData Summary + public MiscData Misc { get { - if (_summary == null || (DateTime.UtcNow > _summaryRefresh)) + if (_misc == null || (DateTime.UtcNow > _miscRefresh)) { - lock (_summaryLock) + lock (_miscLock) { // Thread double locking - if (_summary == null || (DateTime.UtcNow > _summaryRefresh)) + if (_misc == null || (DateTime.UtcNow > _miscRefresh)) { - _summary = BuildSummaryData(GetDataFromProfitTrailer("api/v2/data/misc")); - _summaryRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + _misc = BuildMiscData(GetDataFromProfitTrailer("api/v2/data/misc")); + _miscRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); } } } - return _summary; + return _misc; } } - private SummaryData BuildSummaryData(dynamic PTData) + private MiscData BuildMiscData(dynamic PTData) { - return new SummaryData() + return new MiscData() { Market = PTData.market, FiatConversionRate = PTData.priceDataFiatConversionRate, @@ -119,6 +123,8 @@ namespace Core.Main.DataObjects PendingValue = PTData.totalPendingCurrentValue, DustValue = PTData.totalDustCurrentValue, StartBalance = PTData.startBalance, + TotalCurrentValue = PTData.totalCurrentValue, + TimeZoneOffset = PTData.timeZoneOffset, }; } public PropertiesData Properties @@ -154,27 +160,6 @@ namespace Core.Main.DataObjects BaseUrl = PTProperties.baseUrl }; } - // public StatsData Stats - // { - // get - // { - // if (_stats == null || DateTime.UtcNow > _statsRefresh) - // { - // lock (_statsLock) - // { - // if (_stats == null || DateTime.UtcNow > _statsRefresh) - // { - // dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); - // JObject statsDataJObject = statsDataJson as JObject; - // JObject basicSection = (JObject)statsDataJObject["basic"]; - // _stats = BuildStatsData(basicSection); - // _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); - // } - // } - // } - // return _stats; - // } - // } public StatsData Stats { @@ -239,37 +224,90 @@ namespace Core.Main.DataObjects FundingTotal = statsDataJson["totalFunding"] }; } - // public List DailyPNL - // { - // get - // { - // if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) - // { - // lock (_dailyPNLLock) - // { - // if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) - // { - // dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); - // JObject dailyPNLDataJObject = dailyPNLDataJson as JObject; - // JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"]; - // _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); - // _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); - // } - // } - // } - // return _dailyPNL; - // } - // } - public List DailyPNL + { + get + { + if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + { + lock (_dailyPNLLock) + { + if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + { + 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 && + ((_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 dailyPNLSection = (JArray)extraSection["dailyPNLStats"]; + _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); + _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + } + } + } + return _dailyPNL; + } + } + public int GetTotalDays() + { + return DailyPNL?.Count ?? 0; + } + private DailyPNLData BuildDailyPNLData(dynamic dailyPNLDataJson) + { + return new DailyPNLData() + { + Date = dailyPNLDataJson["date"], + CumulativeProfitCurrency = dailyPNLDataJson["cumulativeProfitCurrency"], + Order = dailyPNLDataJson["order"], + }; + } + public List MonthlyStats { get { - if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + if (_monthlyStats == null || DateTime.UtcNow > _monthlyStatsRefresh) { - lock (_dailyPNLLock) + lock (_monthlyStatsLock) { - if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) + if (_monthlyStats == null || DateTime.UtcNow > _monthlyStatsRefresh) { using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats")) using (var reader = new StreamReader(stream)) @@ -300,36 +338,43 @@ 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())))) + 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(); + //_totalProfit = basicSection["totalProfit"].Value(); + //_totalSales = basicSection["totalSales"].Value(); if (extraSection != null) { - JArray dailyPNLSection = (JArray)extraSection["dailyPNLStats"]; - _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); - _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + JArray monthlyStatsSection = (JArray)extraSection["monthlyStats"]; + _monthlyStats = monthlyStatsSection.Select(j => BuildMonthlyStatsData(j as JObject)).ToList(); + _monthlyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); } } } } } } - return _dailyPNL; + return _monthlyStats; } } - private DailyPNLData BuildDailyPNLData(dynamic dailyPNLDataJson) + + public int GetTotalMonths() { - return new DailyPNLData() + return MonthlyStats?.Count ?? 0; + } + + private MonthlyStatsData BuildMonthlyStatsData(dynamic monthlyStatsDataJson) + { + return new MonthlyStatsData() { - Date = dailyPNLDataJson["date"], - CumulativeProfitCurrency = dailyPNLDataJson["cumulativeProfitCurrency"], - Order = dailyPNLDataJson["order"], + Month = monthlyStatsDataJson["month"], + TotalSales = monthlyStatsDataJson["totalSales"], + TotalProfitCurrency = monthlyStatsDataJson["totalProfitCurrency"], + AvgGrowth = monthlyStatsDataJson["avgGrowth"], }; } public List SellLog @@ -459,33 +504,33 @@ namespace Core.Main.DataObjects public double GetCurrentBalance() { return - (this.Summary.Balance); + (this.Misc.Balance); } public double GetPairsBalance() { return - (this.Summary.PairsValue); + (this.Misc.PairsValue); } public double GetDCABalance() { return - (this.Summary.DCAValue); + (this.Misc.DCAValue); } public double GetPendingBalance() { return - (this.Summary.PendingValue); + (this.Misc.PendingValue); } public double GetDustBalance() { return - (this.Summary.DustValue); + (this.Misc.DustValue); } public double GetSnapshotBalance(DateTime snapshotDateTime) { - double result = _summary.StartBalance; + double result = _misc.StartBalance; result += this.SellLog.FindAll(sl => sl.SoldDate.Date < snapshotDateTime.Date).Sum(sl => sl.Profit); result += this.TransactionData.Transactions.FindAll(t => t.UTCDateTime < snapshotDateTime).Sum(t => t.Amount); diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index c42f770..1270cd0 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -1117,35 +1117,6 @@ namespace Core.Main } } - //private void GetMainFiatCurrencyDetails() - //{ - //this.LastRuntimeSummary.MainFiatCurrency = this.PropertiesData.Currency; - //this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = this.LastMainFiatCurrencyExchangeRate; - - // if (this.LastFiatCurrencyCheck < DateTime.UtcNow.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) - // { - // try - // { - // this.LastRuntimeSummary.MainFiatCurrency = this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency; - // this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = BaseAnalyzer.GetMainFiatCurrencyRate(this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency, this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey, this.Log); - // this.LastMainFiatCurrency = this.LastRuntimeSummary.MainFiatCurrency; - // this.LastMainFiatCurrencyExchangeRate = this.LastRuntimeSummary.MainFiatCurrencyExchangeRate; - - // this.LastFiatCurrencyCheck = DateTime.UtcNow; - // } - // catch (Exception ex) - // { - - // // Fallback to USD in case something went wrong - // this.Log.DoLogError("Fixer.io exchange rate check error: " + ex.Message); - // this.LastRuntimeSummary.MainFiatCurrency = "USD"; - // this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = 1; - // this.LastMainFiatCurrency = "USD"; - // this.LastMainFiatCurrencyExchangeRate = 1; - // } - // } - //} - // Get current PT properties private void LoadCurrentProfitTrailerProperties() { diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index 2311024..00b47c5 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -26,7 +26,7 @@ } } - +
Total Current Value:   @totalCurrentValueString @Model.Summary.MainMarket Starting Value:   @Model.SummaryData.StartBalance   @Model.Summary.MainMarket Starting Value:   @Model.MiscData.StartBalance   @Model.Summary.MainMarket
@@ -50,7 +50,6 @@

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.

@@ -59,47 +58,49 @@
-

Sales Analysis

@{ - double totalProfit = 0; - totalProfit = Model.PTData.SellLog.Sum(s => s.Profit); - double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2); - double percentGain = Math.Round(totalProfit / Model.SummaryData.StartBalance * 100, 2); - double avgDailyGain = Model.DailyGains.Values.Average(dg => dg); - double avgMonthlyGain = Model.MonthlyGains.Values.Average(dg => dg); - string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; - if (Model.PTData.TransactionData.Transactions.Count > 0) { - percentGainText = ""; - } - } + int totalDays = Model.PTData.GetTotalDays(); + double totalSales = Model.PTData.Stats.TotalSales; + double totalProfit = Model.PTData.Stats.TotalProfit; + double totalFundingFees = Model.PTData.Stats.FundingTotal; + double totalPercentGain = Model.PTData.Stats.TotalProfitPerc; + double totalProfitFiat = @Math.Round((totalProfit + totalFundingFees) * Model.PTData.Misc.FiatConversionRate, 0); + double avgDailySales = @Math.Round(totalSales/totalDays, 1); + double avgDailyGain = totalPercentGain / totalDays; + var MonthlyAveragesResult = Model.MonthlyAverages(Model.PTData.MonthlyStats, Model.PTData.DailyPNL); + string startDate = MonthlyAveragesResult.startDate.ToString("d"); + string endDate = MonthlyAveragesResult.endDate.ToString("d"); + double totalMonths = Math.Round(MonthlyAveragesResult.totalMonths, 1); + double avgMonthlySales = @Math.Round(totalSales / totalMonths, 1); + double avgMonthlyProfit = totalProfit / totalMonths; + double avgMonthlyGain = totalPercentGain / totalMonths; + double avgMonthlyFunding = totalFundingFees / totalMonths; + string percentGainText = totalPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; + } +

Sales Analysis

(@startDate - @endDate) - - + + - - - + + + - + - - - - - - - - + + @@ -107,6 +108,21 @@ + @if(Model.PropertiesData.IsLeverageExchange) + { + + + + + + + } + + + + + +
TotalAVG/DayAVG/MonthAVG/Day (@totalDays Days)AVG/Month (@totalMonths Months)
Sales@Model.PTData.SellLog.Count@Math.Round((double)Model.PTData.SellLog.Count / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date).ToList().Count, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))@Math.Round((double)Model.PTData.SellLog.Count / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date.ToString("yyyy-MM")).ToList().Count, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))@totalSales@avgDailySales@avgMonthlySales
ProfitProfit @Model.PTData.Misc.Market @totalProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Math.Round(totalProfit / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date).ToList().Count, 8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Math.Round(totalProfit / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date.ToString("yyyy-MM")).ToList().Count, 8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))
Profit USD@Html.Raw(Model.MainFiatCurrencySymbol + totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(totalProfitFiat / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date).ToList().Count, 8).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(totalProfitFiat / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date.ToString("yyyy-MM")).ToList().Count, 8).ToString("#,#0.00", 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"))
% Gain@avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% @avgMonthlyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
Funding Fees@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@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")))
@@ -127,7 +143,7 @@ Est. Balance - Est. @Model.Summary.MainFiatCurrency Value + Est. @Model.PTData.Properties.Currency Value Est. Gain @@ -135,25 +151,25 @@ 1 month @estimatedBalance1Month.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance1Month * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @Html.Raw(Math.Round(estimatedBalance1Month * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))) @Math.Round((estimatedBalance1Month - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% 3 months @estimatedBalance3Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance3Months * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @Html.Raw(Math.Round(estimatedBalance3Months * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))) @Math.Round((estimatedBalance3Months - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% 6 months @estimatedBalance6Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance6Months * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @Html.Raw(Math.Round(estimatedBalance6Months * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))) @Math.Round((estimatedBalance6Months - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% 1 year @estimatedBalance1Year.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance1Year * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @Html.Raw(Math.Round(estimatedBalance1Year * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))) @Math.Round((estimatedBalance1Year - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% diff --git a/Monitor/Pages/SalesAnalyzer.cshtml.cs b/Monitor/Pages/SalesAnalyzer.cshtml.cs index 049fdb2..58151ee 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml.cs +++ b/Monitor/Pages/SalesAnalyzer.cshtml.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.AspNetCore.Mvc.RazorPages; using Core.Main; -using Core.Helper; +using System.Globalization; using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; @@ -12,7 +11,12 @@ namespace Monitor.Pages public class SalesAnalyzer : _Internal.BasePageModelSecure { public ProfitTrailerData PTData = null; - public SummaryData SummaryData { get; set; } + public MiscData MiscData { get; set; } + public PropertiesData PropertiesData { get; set; } + public StatsData StatsData { get; set; } + public List DailyPNL { get; set; } + public List MonthlyStats { get; set; } + public string TradesChartDataJSON = ""; public string ProfitChartDataJSON = ""; public string BalanceChartDataJSON = ""; @@ -22,17 +26,26 @@ namespace Monitor.Pages public Dictionary MonthlyGains = new Dictionary(); public DateTimeOffset DateTimeNow = Constants.confMinDate; public double totalCurrentValue = 0; + public void OnGet() { base.Init(); BindData(); - BuildTCV(); } + private void BindData() { PTData = this.PtDataObject; - SummaryData = this.PTData.Summary; + MiscData = this.PTData.Misc; + PropertiesData = this.PTData.Properties; + StatsData = this.PTData.Stats; + MonthlyStats = this.PTData.MonthlyStats; + DailyPNL = this.PTData.DailyPNL; + + //List monthlyStatsData = this.PTData.MonthlyStats; + //List dailyPNLData = this.PTData.DailyPNL; + // Convert local offset time to UTC TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); @@ -40,6 +53,49 @@ namespace Monitor.Pages BuildTopMarkets(); BuildSalesChartData(); + BuildTCV(); + //MonthlyAverages(monthlyStatsData, PTData.Stats.FundingTotal); + } + + public (double totalMonths, DateTime startDate, DateTime endDate) MonthlyAverages(List monthlyStats, List dailyPNL) + { + double totalMonths = 0; + // Get the exact start and end dates of sales data + DateTime startDate = dailyPNL.Min(d => DateTime.ParseExact(d.Date, "d-M-yyyy", CultureInfo.InvariantCulture)); + DateTime endDate = dailyPNL.Max(d => DateTime.ParseExact(d.Date, "d-M-yyyy", CultureInfo.InvariantCulture)); + + int daysInFirstMonth = DateTime.DaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1; + int daysInLastMonth = endDate.Day; + //Console.WriteLine("Start Date: {0}, End Date: {1}, Days in first month: {2}, Days in last month: {3}", startDate, endDate, daysInFirstMonth, daysInLastMonth); + + for (int i = 0; i < monthlyStats.Count; i++) + { + var monthStat = monthlyStats[i]; + double weight; + + // Parse the Month property into a DateTime object + DateTime monthDate = DateTime.ParseExact(monthStat.Month, "M-yyyy", CultureInfo.InvariantCulture); + + // If it's the first or last month in the dataset, calculate the weight based on the number of days + if (i == 0) + { + // Calculate weight based on the number of days in the dataset for the first month + weight = daysInFirstMonth / 30.0; + } + else if (i == monthlyStats.Count - 1) + { + // Calculate weight based on the number of days in the dataset for the last month + weight = (daysInLastMonth / 30.0); + } + else + { + // Otherwise, assume it's a full month + weight = 1; + } + totalMonths += weight; + //Console.WriteLine("Month: {0}, Weight: {1}", monthStat.Month, weight); + } + return (totalMonths, startDate, endDate); } private void BuildTopMarkets() { diff --git a/Monitor/Pages/SettingsGeneral.cshtml.cs b/Monitor/Pages/SettingsGeneral.cshtml.cs index a41c1cb..d0444e0 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml.cs +++ b/Monitor/Pages/SettingsGeneral.cshtml.cs @@ -13,7 +13,7 @@ namespace Monitor.Pages { public string ValidationMessage = ""; public ProfitTrailerData PTData = null; - public SummaryData SummaryData { get; set; } + public MiscData MiscData { get; set; } private string GetTimezoneOffsetString(TimeZoneInfo tzi) { @@ -55,7 +55,7 @@ namespace Monitor.Pages { base.Init(); PTData = this.PtDataObject; - SummaryData = this.PTData.Summary; + MiscData = this.PTData.Misc; string notification = GetStringParameter("n", ""); if (notification.Equals("BackupRestored")) diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index b4cf026..66ee7fa 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -25,7 +25,7 @@
-
+
@{ string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); if (Model.totalCurrentValue > 100) { @@ -35,8 +35,8 @@
- Start:   @Model.SummaryData.StartBalance @Model.Summary.MainMarket -     Gain: @Math.Round(((Model.totalCurrentValue - Model.SummaryData.StartBalance) / Model.SummaryData.StartBalance) * 100, 2)% + Start:   @Model.MiscData.StartBalance @Model.Summary.MainMarket +     Gain: @Math.Round(((Model.totalCurrentValue - Model.MiscData.StartBalance) / Model.MiscData.StartBalance) * 100, 2)%
@@ -166,12 +166,12 @@ var totalProfitPercent = overviewStats.TotalProfitPerc; //var totalFundingGain = totalProfitPercent * ((totalProfit + totalFunding) / totalProfit); - double todaysProfitFiat = Math.Round((todaysProfit + todaysFunding) * Model.PTData.Summary.FiatConversionRate, 2); - double yesterdaysProfitFiat = Math.Round((yesterdaysProfit + yesterdaysFunding) * Model.PTData.Summary.FiatConversionRate, 2); - double last7DaysProfitFiat = Math.Round((last7DaysProfit + last7DaysFunding) * Model.PTData.Summary.FiatConversionRate, 2); - double thisMonthProfitFiat = Math.Round((thisMonthProfit + thisMonthFunding) * Model.PTData.Summary.FiatConversionRate, 2); - double lastMonthProfitFiat = Math.Round((lastMonthProfit + lastMonthFunding) * Model.PTData.Summary.FiatConversionRate, 2); - double totalProfitFiat = Math.Round((totalProfit + totalFunding) * Model.PTData.Summary.FiatConversionRate, 2); + double todaysProfitFiat = Math.Round((todaysProfit + todaysFunding) * Model.PTData.Misc.FiatConversionRate, 2); + double yesterdaysProfitFiat = Math.Round((yesterdaysProfit + yesterdaysFunding) * Model.PTData.Misc.FiatConversionRate, 2); + double last7DaysProfitFiat = Math.Round((last7DaysProfit + last7DaysFunding) * Model.PTData.Misc.FiatConversionRate, 2); + double thisMonthProfitFiat = Math.Round((thisMonthProfit + thisMonthFunding) * Model.PTData.Misc.FiatConversionRate, 2); + double lastMonthProfitFiat = Math.Round((lastMonthProfit + lastMonthFunding) * Model.PTData.Misc.FiatConversionRate, 2); + double totalProfitFiat = Math.Round((totalProfit + totalFunding) * Model.PTData.Misc.FiatConversionRate, 2); bool futuresFunding = Model.PropertiesData.IsLeverageExchange; @@ -181,7 +181,7 @@ Sales - Profit @Model.PTData.Summary.Market + Profit @Model.PTData.Misc.Market Gain @if (futuresFunding) { diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index 3220282..d74916d 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -17,8 +17,7 @@ namespace Monitor.Pages public ProfitTrailerData PTData = null; public StatsData StatsData { get; set; } public PropertiesData PropertiesData { get; set; } - public SummaryData SummaryData { get; set; } - + public MiscData MiscData { get; set; } public List MarketTrends { get; set; } = new List(); public double DataHours { get; set; } public int ProfitDays { get; set; } @@ -42,8 +41,7 @@ namespace Monitor.Pages PTData = this.PtDataObject; StatsData = this.PTData.Stats; PropertiesData = this.PTData.Properties; - SummaryData = this.PTData.Summary; - + MiscData = this.PTData.Misc; List dailyPNLData = this.PTData.DailyPNL; // Cleanup temp files @@ -215,7 +213,7 @@ namespace Monitor.Pages // Convert the list to a JSON string using Newtonsoft.Json ProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { new { - key = "Profit in " + PTData.Properties.Currency, + key = "Profit in " + PTData.Misc.Market, color = Constants.ChartLineColors[1], values = profitPerDayList } From 615e07986bd710cde5681ed851180adc6d8cb057 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Wed, 17 Jan 2024 01:56:52 +0900 Subject: [PATCH 07/14] Sales Analyzer Updates --- Core/DataObjects/PTMagicData.cs | 12 + Core/DataObjects/ProfitTrailerData.cs | 92 ++++++- Monitor/Pages/SalesAnalyzer.cshtml | 264 +++++++++++++++---- Monitor/Pages/SalesAnalyzer.cshtml.cs | 211 +++++++++++++-- Monitor/Pages/_get/DashboardBottom.cshtml | 46 ++-- Monitor/Pages/_get/DashboardBottom.cshtml.cs | 249 ++++++++--------- 6 files changed, 659 insertions(+), 215 deletions(-) diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index 3e72643..0aebdc2 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -440,6 +440,10 @@ namespace Core.Main.DataObjects.PTMagicData public double FundingThisMonth { get; set; } public double FundingLastMonth { get; set; } public double FundingTotal { get; set; } + public double TotalFundingPerc { get; set; } + public double TotalFundingPercYesterday { get; set; } + public double TotalFundingPercWeek { get; set; } + public double TotalFundingPercToday { get; set; } } public class DailyPNLData @@ -448,6 +452,13 @@ namespace Core.Main.DataObjects.PTMagicData public double CumulativeProfitCurrency { get; set; } public double Order { get; set; } + } + public class DailyTCVData + { + public string Date { get; set; } + public double TCV { get; set; } + public double Order { get; set; } + } public class MonthlyStatsData { @@ -455,6 +466,7 @@ namespace Core.Main.DataObjects.PTMagicData public double TotalProfitCurrency { get; set; } public double TotalSales { get; set; } public double AvgGrowth { get; set; } + public double Order { get; set; } } diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 0894193..d828a54 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -19,6 +19,7 @@ namespace Core.Main.DataObjects private PropertiesData _properties = null; private StatsData _stats = null; private List _dailyPNL = new List(); + private List _dailyTCV = new List(); private List _monthlyStats = new List(); private decimal? _totalProfit = null; public decimal? TotalProfit @@ -39,15 +40,16 @@ namespace Core.Main.DataObjects private PTMagicConfiguration _systemConfiguration = null; private TransactionData _transactionData = null; private DateTime _dailyPNLRefresh = DateTime.UtcNow; + private DateTime _dailyTCVRefresh = DateTime.UtcNow; private DateTime _monthlyStatsRefresh = DateTime.UtcNow; private DateTime _statsRefresh = DateTime.UtcNow; private DateTime _buyLogRefresh = DateTime.UtcNow; private DateTime _sellLogRefresh = DateTime.UtcNow; private DateTime _dcaLogRefresh = DateTime.UtcNow; private DateTime _miscRefresh = DateTime.UtcNow; - private DateTime _propertiesRefresh = DateTime.UtcNow; - private volatile object _dailyStatsLock = new object(); - private volatile object _dailyPNLLock = new object(); + private DateTime _propertiesRefresh = DateTime.UtcNow; + private volatile object _dailyPNLLock = new object(); + private volatile object _dailyTCVLock = new object(); private volatile object _monthlyStatsLock = new object(); private volatile object _statsLock = new object(); private volatile object _buyLock = new object(); @@ -221,7 +223,11 @@ namespace Core.Main.DataObjects FundingWeek = statsDataJson["totalFundingWeek"], FundingThisMonth = statsDataJson["totalFundingThisMonth"], FundingLastMonth = statsDataJson["totalFundingLastMonth"], - FundingTotal = statsDataJson["totalFunding"] + FundingTotal = statsDataJson["totalFunding"], + TotalFundingPerc = statsDataJson["totalFundingPerc"], + TotalFundingPercYesterday = statsDataJson["totalFundingPercYesterday"], + TotalFundingPercWeek = statsDataJson["totalFundingPercWeekPerc"], + TotalFundingPercToday = statsDataJson["totalFundingPercTodayPerc"] }; } public List DailyPNL @@ -299,6 +305,83 @@ namespace Core.Main.DataObjects Order = dailyPNLDataJson["order"], }; } + + + public List DailyTCV + { + get + { + if (_dailyTCV == null || DateTime.UtcNow > _dailyTCVRefresh) + { + lock (_dailyTCVLock) + { + if (_dailyTCV == null || DateTime.UtcNow > _dailyTCVRefresh) + { + 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) // && + //((_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 dailyTCVSection = (JArray)extraSection["dailyTCVStats"]; + _dailyTCV = dailyTCVSection.Select(j => BuildDailyTCVData(j as JObject)).ToList(); + _dailyTCVRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + } + } + } + return _dailyTCV; + } + } + public int GetTotalTCVDays() + { + return DailyTCV?.Count ?? 0; + } + private DailyTCVData BuildDailyTCVData(dynamic dailyTCVDataJson) + { + return new DailyTCVData() + { + Date = dailyTCVDataJson["date"], + TCV = dailyTCVDataJson["TCV"], + Order = dailyTCVDataJson["order"], + }; + } public List MonthlyStats { get @@ -375,6 +458,7 @@ namespace Core.Main.DataObjects TotalSales = monthlyStatsDataJson["totalSales"], TotalProfitCurrency = monthlyStatsDataJson["totalProfitCurrency"], AvgGrowth = monthlyStatsDataJson["avgGrowth"], + Order = monthlyStatsDataJson["order"], }; } public List SellLog diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index 00b47c5..6b5d34f 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -19,14 +19,11 @@ @{ - string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); - if (Model.totalCurrentValue > 100) - { - totalCurrentValueString = Math.Round(Model.totalCurrentValue, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); - } + double totalCurrentValue = double.Parse(Model.PTData.Misc.TotalCurrentValue); + string totalCurrentValueString = Model.PTData.Misc.TotalCurrentValue; } - Total Current Value:   @totalCurrentValueString @Model.Summary.MainMarket - Starting Value:   @Model.MiscData.StartBalance   @Model.Summary.MainMarket + Total Current Value:   @totalCurrentValueString @Model.Summary.MainMarket + Starting Value:   @Model.MiscData.StartBalance   @Model.Summary.MainMarket @@ -34,16 +31,33 @@
-
-
-
-

Cumulative PNL

-
- +
+
+
+

Cumulative Profits + @if (!Model.CumulativeProfitChartDataJSON.Equals("")) { +
+ +
+ } else { +

Unable to load graph, no sales data found.

+ } +

+
+ +
+
+

Daily TCV + @if (!Model.TCVChartDataJSON.Equals("")) { +
+ +
+ } else { +

Unable to load graph, no sales data found.

+ }

-
@if (Model.PTData.SellLog.Count == 0) {
@@ -60,10 +74,11 @@
@{ int totalDays = Model.PTData.GetTotalDays(); + double startBalance = Model.MiscData.StartBalance; double totalSales = Model.PTData.Stats.TotalSales; double totalProfit = Model.PTData.Stats.TotalProfit; double totalFundingFees = Model.PTData.Stats.FundingTotal; - double totalPercentGain = Model.PTData.Stats.TotalProfitPerc; + double totalPercentGain = ((totalProfit + totalFundingFees) / startBalance) * 100; double totalProfitFiat = @Math.Round((totalProfit + totalFundingFees) * Model.PTData.Misc.FiatConversionRate, 0); double avgDailySales = @Math.Round(totalSales/totalDays, 1); double avgDailyGain = totalPercentGain / totalDays; @@ -76,9 +91,8 @@ double avgMonthlyProfit = totalProfit / totalMonths; double avgMonthlyGain = totalPercentGain / totalMonths; double avgMonthlyFunding = totalFundingFees / totalMonths; - string percentGainText = totalPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; } -

Sales Analysis

(@startDate - @endDate) +

Sales Analysis

     (@startDate - @endDate) @@ -101,12 +115,6 @@ - - - - - - @if(Model.PropertiesData.IsLeverageExchange) { @@ -123,6 +131,12 @@ + + + + + +
@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"))
% Gain@Html.Raw(percentGainText)@avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@avgMonthlyGain.ToString("#,#0.00", 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")))
% Gain@totalPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@avgMonthlyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
@@ -131,46 +145,52 @@
@{ - double currentTotalBalance = Model.totalCurrentValue; - double estimatedBalance1Month = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 30.0), 8); - double estimatedBalance3Months = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 90.0), 8); - double estimatedBalance6Months = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 180.0), 8); - double estimatedBalance1Year = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 365.0), 8); + double estimatedBalance1Week = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 7.0), 8); + double estimatedBalance1Month = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 30.0), 8); + double estimatedBalance3Months = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 90.0), 8); + double estimatedBalance6Months = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 180.0), 8); + double estimatedBalance1Year = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 365.0), 8); } -

Balance Prediction

+

TCV Prediction

- + - + + + + + + + - + - + - + - + - + - + - +
Est. BalanceEst. TCV Est. @Model.PTData.Properties.Currency Value Est. Gain
1 month1 Week@estimatedBalance1Week.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@Html.Raw(Math.Round(estimatedBalance1Week * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))@Math.Round((estimatedBalance1Week - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
1 Month @estimatedBalance1Month.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @Html.Raw(Math.Round(estimatedBalance1Month * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))@Math.Round((estimatedBalance1Month - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@Math.Round((estimatedBalance1Month - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
3 months3 Months @estimatedBalance3Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @Html.Raw(Math.Round(estimatedBalance3Months * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))@Math.Round((estimatedBalance3Months - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@Math.Round((estimatedBalance3Months - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
6 months6 Months @estimatedBalance6Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @Html.Raw(Math.Round(estimatedBalance6Months * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))@Math.Round((estimatedBalance6Months - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@Math.Round((estimatedBalance6Months - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
1 year1 Year @estimatedBalance1Year.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @Html.Raw(Math.Round(estimatedBalance1Year * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))@Math.Round((estimatedBalance1Year - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%@Math.Round((estimatedBalance1Year - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
@@ -180,20 +200,27 @@
-
+
+

Daily Sales
- +

-
-
- -
+
+

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

Unable to load graph, no sales data found.

+ }

-
+
+
@@ -375,7 +402,7 @@ nv.utils.windowResize(lineChart.update); return lineChart; }); - +@* nv.addGraph(function () { var lineChart = nv.models.lineChart(); var height = 300; @@ -402,7 +429,7 @@ d3.select('.balance-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart); nv.utils.windowResize(lineChart.update); return lineChart; - }); + }); *@ $("#salesList").on("show.bs.modal", function (e) { $(this).find(".modal-content").html(''); @@ -412,7 +439,146 @@ }); }); })(jQuery); - - + + + + + + } diff --git a/Monitor/Pages/SalesAnalyzer.cshtml.cs b/Monitor/Pages/SalesAnalyzer.cshtml.cs index 58151ee..89cb5da 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml.cs +++ b/Monitor/Pages/SalesAnalyzer.cshtml.cs @@ -15,9 +15,14 @@ namespace Monitor.Pages public PropertiesData PropertiesData { get; set; } public StatsData StatsData { get; set; } public List DailyPNL { get; set; } + public List DailyTCV { get; set; } + public int ProfitDays { get; set; } + public int TCVDays { get; set; } public List MonthlyStats { get; set; } public string TradesChartDataJSON = ""; + public string CumulativeProfitChartDataJSON = ""; + public string TCVChartDataJSON = ""; public string ProfitChartDataJSON = ""; public string BalanceChartDataJSON = ""; public IEnumerable> TopMarkets = null; @@ -42,6 +47,7 @@ namespace Monitor.Pages StatsData = this.PTData.Stats; MonthlyStats = this.PTData.MonthlyStats; DailyPNL = this.PTData.DailyPNL; + DailyTCV = this.PTData.DailyTCV; //List monthlyStatsData = this.PTData.MonthlyStats; //List dailyPNLData = this.PTData.DailyPNL; @@ -53,9 +59,198 @@ namespace Monitor.Pages BuildTopMarkets(); BuildSalesChartData(); - BuildTCV(); + BuildProfitChartData(); + BuildCumulativeProfitChartData(); + BuildTCVChartData(); //MonthlyAverages(monthlyStatsData, PTData.Stats.FundingTotal); } + private void BuildTCVChartData() + { + List TCVPerDayList = new List(); + + if (PTData.DailyTCV.Count > 0) + { + // Get timezone offset + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); + + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) + { + offset = TimeSpan.Zero; // If offset is invalid, set it to zero + } + + DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date; + + // Parse dates once and adjust them to the local timezone + Dictionary dailyTCVByDate = PTData.DailyTCV + .Select(data => { + DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture); + DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset); + return new { Date = dateLocal.Date, Data = data }; + }) + .ToDictionary( + item => item.Date, + item => item.Data + ); + + DateTime earliestDataDate = dailyTCVByDate.Keys.Min(); + DateTime startDate = earliestDataDate; + + // Calculate the total days of data available + TCVDays = (endDate - startDate).Days; + + for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) + { + // Use the dictionary to find the Data for the date + if (dailyTCVByDate.TryGetValue(date, out DailyTCVData dailyTCV)) + { + double TCV = dailyTCV.TCV; + + // Add the data point to the list + TCVPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = TCV }); + } + } + // Convert the list to a JSON string using Newtonsoft.Json + TCVChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { + new { + key = "TCV in " + PTData.Misc.Market, + color = Constants.ChartLineColors[1], + values = TCVPerDayList + } + }); + } + } + + private void BuildCumulativeProfitChartData() + { + List profitPerDayList = new List(); + + if (PTData.DailyPNL.Count > 0) + { + // Get timezone offset + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); + + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) + { + offset = TimeSpan.Zero; // If offset is invalid, set it to zero + } + + DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date; + + // Parse dates once and adjust them to the local timezone + Dictionary dailyPNLByDate = PTData.DailyPNL + .Select(data => { + DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture); + DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset); + return new { Date = dateLocal.Date, Data = data }; + }) + .ToDictionary( + item => item.Date, + item => item.Data + ); + + DateTime earliestDataDate = dailyPNLByDate.Keys.Min(); + DateTime startDate = earliestDataDate; + + // Calculate the total days of data available + ProfitDays = (endDate - startDate).Days; + + double previousDayCumulativeProfit = 0; + for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) + { + // Use the dictionary to find the DailyPNLData for the date + if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL)) + { + // Use the CumulativeProfitCurrency directly + double profitFiat = dailyPNL.CumulativeProfitCurrency; + + // Add the data point to the list + profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat }); + + previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency; + } + } + // Convert the list to a JSON string using Newtonsoft.Json + CumulativeProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { + new { + key = "Profit in " + PTData.Misc.Market, + color = Constants.ChartLineColors[1], + values = profitPerDayList + } + }); + } + } + private void BuildProfitChartData() + { + List profitPerDayList = new List(); + + if (PTData.DailyPNL.Count > 0) + { + // Get timezone offset + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); + + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) + { + offset = TimeSpan.Zero; // If offset is invalid, set it to zero + } + + DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date; + + // Parse dates once and adjust them to the local timezone + Dictionary dailyPNLByDate = PTData.DailyPNL + .Select(data => { + DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture); + DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset); + return new { Date = dateLocal.Date, Data = data }; + }) + .ToDictionary( + item => item.Date, + item => item.Data + ); + + DateTime earliestDataDate = dailyPNLByDate.Keys.Min(); + DateTime startDate = earliestDataDate; + + // Calculate the total days of data available + ProfitDays = (endDate - startDate).Days; + + double previousDayCumulativeProfit = 0; + bool isFirstDay = true; + for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) + { + // Use the dictionary to find the DailyPNLData for the date + if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL)) + { + if (isFirstDay) + { + isFirstDay = false; + } + else + { + // Calculate the profit for the current day + double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2); + + // Add the data point to the list + profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat }); + } + previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency; + } + } + // Convert the list to a JSON string using Newtonsoft.Json + ProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { + new { + key = "Profit in " + PTData.Misc.Market, + color = Constants.ChartLineColors[1], + values = profitPerDayList + } + }); + } + } public (double totalMonths, DateTime startDate, DateTime endDate) MonthlyAverages(List monthlyStats, List dailyPNL) { @@ -186,19 +381,5 @@ namespace Monitor.Pages } } } - private void BuildTCV() - { - double AvailableBalance = PTData.GetCurrentBalance(); - foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog) - { - double leverage = dcaLogEntry.Leverage; - if (leverage == 0) - { - leverage = 1; - } - totalCurrentValue = totalCurrentValue + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) / leverage); - } - totalCurrentValue = totalCurrentValue + AvailableBalance; - } } } diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index 66ee7fa..2e5fd8e 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -11,7 +11,7 @@
-
+

Market Trend History

@if (!Model.TrendChartDataJSON.Equals("")) {
@@ -24,7 +24,7 @@
-
+
@{ string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); @@ -127,44 +127,42 @@

Sales Overviewmore

@{ + double avgGrowthThisMonth = Model.PTData.MonthlyStats.FirstOrDefault(data => data.Order == 1)?.AvgGrowth ?? 0.0; + double avgGrowthLastMonth = Model.PTData.MonthlyStats.FirstOrDefault(data => data.Order == 2)?.AvgGrowth ?? 0.0; - var overviewStats = Model.StatsData; // todaysStats is a new variable + //var startingBalance = Model.MiscData.StartBalance; + var totalCurrentValue = Model.totalCurrentValue; + var overviewStats = Model.StatsData; var todaysSales = overviewStats.SalesToday; var todaysProfit = overviewStats.ProfitToday; var todaysFunding = overviewStats.FundingToday; - var todaysPercentGain = overviewStats.ProfitPercToday; - //var todaysFundingGain = todaysPercentGain * ((todaysProfit - todaysFunding) / todaysProfit); + var todaysPercentGain = overviewStats.ProfitPercToday + Model.PTData.Stats.TotalFundingPercToday; var yesterdaysSales = overviewStats.SalesYesterday; var yesterdaysProfit = overviewStats.ProfitYesterday; var yesterdaysFunding = overviewStats.FundingYesterday; - var yesterdaysPercentGain = overviewStats.ProfitPercYesterday; - //var yesterdaysFundingGain = yesterdaysPercentGain * ((yesterdaysProfit + yesterdaysFunding) / yesterdaysProfit); + var yesterdaysPercentGain = overviewStats.ProfitPercYesterday + Model.PTData.Stats.TotalFundingPercYesterday; var last7DaysSales = overviewStats.SalesWeek; var last7DaysProfit = overviewStats.ProfitWeek; var last7DaysFunding = overviewStats.FundingWeek; - var last7DaysPercentGain = overviewStats.ProfitPercWeek; - //var last7DaysFundingGain = last7DaysPercentGain * ((last7DaysProfit + last7DaysFunding) / last7DaysProfit); + var last7DaysPercentGain = overviewStats.ProfitPercWeek + Model.PTData.Stats.TotalFundingPercWeek; var thisMonthSales = overviewStats.SalesThisMonth; var thisMonthProfit = overviewStats.ProfitThisMonth; var thisMonthFunding = overviewStats.FundingThisMonth; - var thisMonthPercentGain = overviewStats.ProfitPercThisMonth; - //var thisMonthFundingGain = thisMonthPercentGain * ((thisMonthProfit + thisMonthFunding) / thisMonthProfit); + var thisMonthPercentGain = avgGrowthThisMonth; var lastMonthSales = overviewStats.SalesLastMonth; var lastMonthProfit = overviewStats.ProfitLastMonth; var lastMonthFunding = overviewStats.FundingLastMonth; - var lastMonthPercentGain = overviewStats.ProfitPercLastMonth; - //var lastMonthFundingGain = lastMonthPercentGain * ((lastMonthProfit + lastMonthFunding) / lastMonthProfit); + var lastMonthPercentGain = avgGrowthLastMonth; var totalSales = overviewStats.TotalSales; var totalProfit = overviewStats.TotalProfit; var totalFunding = overviewStats.FundingTotal; - var totalProfitPercent = overviewStats.TotalProfitPerc; - //var totalFundingGain = totalProfitPercent * ((totalProfit + totalFunding) / totalProfit); + var totalPercentGain = overviewStats.TotalProfitPerc + Model.PTData.Stats.TotalFundingPerc; double todaysProfitFiat = Math.Round((todaysProfit + todaysFunding) * Model.PTData.Misc.FiatConversionRate, 2); double yesterdaysProfitFiat = Math.Round((yesterdaysProfit + yesterdaysFunding) * Model.PTData.Misc.FiatConversionRate, 2); @@ -182,12 +180,12 @@ Sales Profit @Model.PTData.Misc.Market - Gain @if (futuresFunding) { - Funding + Funding @Model.PTData.Misc.Market } @Model.PTData.Properties.Currency + Gain @@ -195,67 +193,67 @@ Today @overviewStats.SalesToday @todaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @todaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% @if (futuresFunding) { @todaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) } @Html.Raw(todaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @todaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% Yesterday @yesterdaysSales @yesterdaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @yesterdaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% @if (futuresFunding) { @yesterdaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - } + } @Html.Raw(yesterdaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @yesterdaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% Last 7 Days @last7DaysSales @last7DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @last7DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% @if (futuresFunding) { @last7DaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) } @Html.Raw(last7DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @last7DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% This Month @thisMonthSales @thisMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @thisMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% @if (futuresFunding) { @thisMonthFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) } @Html.Raw(thisMonthProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @thisMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% Last Month @lastMonthSales @lastMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @lastMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% @if (futuresFunding) { @lastMonthFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) } @Html.Raw(lastMonthProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @lastMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% Total @totalSales @totalProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @totalProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% @if (futuresFunding) { @totalFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) } @Html.Raw(totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) + @totalPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index d74916d..970e0bd 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -42,7 +42,9 @@ namespace Monitor.Pages StatsData = this.PTData.Stats; PropertiesData = this.PTData.Properties; MiscData = this.PTData.Misc; + List monthlyStatsData = this.PTData.MonthlyStats; List dailyPNLData = this.PTData.DailyPNL; + // Cleanup temp files FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5); @@ -65,161 +67,162 @@ namespace Monitor.Pages BuildAssetDistributionData(); BuildProfitChartData(); } + private void BuildMarketTrendChartData() -{ - List trendChartData = new List(); - if (MarketTrends.Count > 0) { - - int mtIndex = 0; - foreach (MarketTrend mt in MarketTrends) + List trendChartData = new List(); + if (MarketTrends.Count > 0) { - if (mt.DisplayGraph) + + int mtIndex = 0; + foreach (MarketTrend mt in MarketTrends) { - string lineColor = mtIndex < Constants.ChartLineColors.Length - ? Constants.ChartLineColors[mtIndex] - : Constants.ChartLineColors[mtIndex - 20]; - - if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) + if (mt.DisplayGraph) { - List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; + string lineColor = mtIndex < Constants.ChartLineColors.Length + ? Constants.ChartLineColors[mtIndex] + : Constants.ChartLineColors[mtIndex - 20]; - if (marketTrendChangeSummaries.Count > 0) + if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) { - List trendValues = new List(); + List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; - // Sort marketTrendChangeSummaries by TrendDateTime - marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList(); - - // Get trend ticks for chart - TimeSpan offset; - bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); - string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); - - if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) + if (marketTrendChangeSummaries.Count > 0) { - offset = TimeSpan.Zero; // If offset is invalid, set it to zero - } + List trendValues = new List(); - DateTime currentDateTime = DateTime.UtcNow; - DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); - DateTime endDateTime = currentDateTime; + // Sort marketTrendChangeSummaries by TrendDateTime + marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList(); - // Ensure startDateTime doesn't exceed the available data - DateTime earliestTrendDateTime = marketTrendChangeSummaries.Min(mtc => mtc.TrendDateTime); - startDateTime = startDateTime > earliestTrendDateTime ? startDateTime : earliestTrendDateTime; - DataHours = (currentDateTime - earliestTrendDateTime).TotalHours; + // Get trend ticks for chart + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); - // Cache the result of SplitCamelCase(mt.Name) - string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name); - - for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) - { - // Use binary search to find the range of items that match the condition - int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer.Create((x, y) => x.TrendDateTime.CompareTo(y.TrendDateTime))); - if (index < 0) index = ~index; - if (index < marketTrendChangeSummaries.Count) + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) { - MarketTrendChange mtc = marketTrendChangeSummaries[index]; - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; - - // Adjust tickTime to the desired timezone before converting to string - DateTime adjustedTickTime = tickTime.Add(isNegative ? -offset : offset); - trendValues.Add("{ x: new Date('" + adjustedTickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + offset = TimeSpan.Zero; // If offset is invalid, set it to zero } + + DateTime currentDateTime = DateTime.UtcNow; + DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); + DateTime endDateTime = currentDateTime; + + // Ensure startDateTime doesn't exceed the available data + DateTime earliestTrendDateTime = marketTrendChangeSummaries.Min(mtc => mtc.TrendDateTime); + startDateTime = startDateTime > earliestTrendDateTime ? startDateTime : earliestTrendDateTime; + DataHours = (currentDateTime - earliestTrendDateTime).TotalHours; + + // Cache the result of SplitCamelCase(mt.Name) + string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name); + + for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + { + // Use binary search to find the range of items that match the condition + int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer.Create((x, y) => x.TrendDateTime.CompareTo(y.TrendDateTime))); + if (index < 0) index = ~index; + if (index < marketTrendChangeSummaries.Count) + { + MarketTrendChange mtc = marketTrendChangeSummaries[index]; + if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; + + // Adjust tickTime to the desired timezone before converting to string + DateTime adjustedTickTime = tickTime.Add(isNegative ? -offset : offset); + trendValues.Add("{ x: new Date('" + adjustedTickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + } + } + + // Add most recent tick + MarketTrendChange latestMtc = marketTrendChangeSummaries.Last(); + if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0; + + // Adjust latestMtc.TrendDateTime to the desired timezone before converting to string + DateTime adjustedLatestTrendDateTime = latestMtc.TrendDateTime.Add(isNegative ? -offset : offset); + trendValues.Add("{ x: new Date('" + adjustedLatestTrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + + // Use cached splitCamelCaseName + trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }"); + mtIndex++; } - - // Add most recent tick - MarketTrendChange latestMtc = marketTrendChangeSummaries.Last(); - if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0; - - // Adjust latestMtc.TrendDateTime to the desired timezone before converting to string - DateTime adjustedLatestTrendDateTime = latestMtc.TrendDateTime.Add(isNegative ? -offset : offset); - trendValues.Add("{ x: new Date('" + adjustedLatestTrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); - - // Use cached splitCamelCaseName - trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }"); - mtIndex++; } } } } + TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]"; } - TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]"; -} private void BuildProfitChartData() -{ - List profitPerDayList = new List(); - - if (PTData.DailyPNL.Count > 0) { - // Get timezone offset - TimeSpan offset; - bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); - string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); + List profitPerDayList = new List(); - if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) + if (PTData.DailyPNL.Count > 0) { - offset = TimeSpan.Zero; // If offset is invalid, set it to zero - } + // Get timezone offset + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); - DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date; - - // Parse dates once and adjust them to the local timezone - Dictionary dailyPNLByDate = PTData.DailyPNL - .Select(data => { - DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture); - DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset); - return new { Date = dateLocal.Date, Data = data }; - }) - .ToDictionary( - item => item.Date, - item => item.Data - ); - - DateTime earliestDataDate = dailyPNLByDate.Keys.Min(); - DateTime startDate = endDate.AddDays(-PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays - 1); // Fetch data for timeframe + 1 days - if (startDate < earliestDataDate) - { - startDate = earliestDataDate; - } - - // Calculate the total days of data available - ProfitDays = (endDate - startDate).Days; - - double previousDayCumulativeProfit = 0; - bool isFirstDay = true; - for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) - { - // Use the dictionary to find the DailyPNLData for the date - if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL)) + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) { - if (isFirstDay) - { - isFirstDay = false; - } - else - { - // Calculate the profit for the current day - double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2); + offset = TimeSpan.Zero; // If offset is invalid, set it to zero + } - // Add the data point to the list - profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat }); + DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date; + + // Parse dates once and adjust them to the local timezone + Dictionary dailyPNLByDate = PTData.DailyPNL + .Select(data => { + DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture); + DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset); + return new { Date = dateLocal.Date, Data = data }; + }) + .ToDictionary( + item => item.Date, + item => item.Data + ); + + DateTime earliestDataDate = dailyPNLByDate.Keys.Min(); + DateTime startDate = endDate.AddDays(-PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays - 1); // Fetch data for timeframe + 1 days + if (startDate < earliestDataDate) + { + startDate = earliestDataDate; + } + + // Calculate the total days of data available + ProfitDays = (endDate - startDate).Days; + + double previousDayCumulativeProfit = 0; + bool isFirstDay = true; + for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) + { + // Use the dictionary to find the DailyPNLData for the date + if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL)) + { + if (isFirstDay) + { + isFirstDay = false; + } + else + { + // Calculate the profit for the current day + double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2); + + // Add the data point to the list + profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat }); + } + previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency; } - previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency; } + // Convert the list to a JSON string using Newtonsoft.Json + ProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { + new { + key = "Profit in " + PTData.Misc.Market, + color = Constants.ChartLineColors[1], + values = profitPerDayList + } + }); } - // Convert the list to a JSON string using Newtonsoft.Json - ProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { - new { - key = "Profit in " + PTData.Misc.Market, - color = Constants.ChartLineColors[1], - values = profitPerDayList - } - }); } -} private void BuildAssetDistributionData() { From b6b8647f2187b52409e9fa029d0b76db1ae43c7d Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Thu, 18 Jan 2024 02:13:09 +0900 Subject: [PATCH 08/14] Sales Analyzer Top Markets --- Core/DataObjects/PTMagicData.cs | 8 +- Core/DataObjects/ProfitTrailerData.cs | 91 ++++++++++++++++- Monitor/Pages/SalesAnalyzer.cshtml | 118 +++++++++------------- Monitor/Pages/SalesAnalyzer.cshtml.cs | 54 +--------- Monitor/Pages/_get/DashboardBottom.cshtml | 9 +- 5 files changed, 149 insertions(+), 131 deletions(-) diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index 0aebdc2..7969a79 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -467,7 +467,13 @@ namespace Core.Main.DataObjects.PTMagicData public double TotalSales { get; set; } public double AvgGrowth { get; set; } public double Order { get; set; } - + } + public class ProfitablePairsData + { + public string Coin { get; set; } + public double ProfitCurrency { get; set; } + public int SoldTimes { get; set; } + public double Avg { get; set; } } public class PTStrategy diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index d828a54..212f8d2 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -21,6 +21,7 @@ namespace Core.Main.DataObjects private List _dailyPNL = new List(); private List _dailyTCV = new List(); private List _monthlyStats = new List(); + private List _profitablePairs = new List(); private decimal? _totalProfit = null; public decimal? TotalProfit { @@ -48,6 +49,7 @@ 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 volatile object _dailyPNLLock = new object(); private volatile object _dailyTCVLock = new object(); private volatile object _monthlyStatsLock = new object(); @@ -56,7 +58,8 @@ namespace Core.Main.DataObjects private volatile object _sellLock = new object(); private volatile object _dcaLock = new object(); private volatile object _miscLock = new object(); - private volatile object _propertiesLock = new object(); + private volatile object _propertiesLock = new object(); + private volatile object _profitablePairsLock = new object(); private TimeSpan? _offsetTimeSpan = null; public void DoLog(string message) { @@ -305,6 +308,90 @@ namespace Core.Main.DataObjects Order = dailyPNLDataJson["order"], }; } + public List ProfitablePairs + { + get + { + if (_profitablePairs == null || DateTime.UtcNow > _profitablePairsRefresh) + { + lock (_profitablePairsLock) + { + if (_profitablePairs == null || DateTime.UtcNow > _profitablePairsRefresh) + { + 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) // && + //((_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) + { + JObject profitablePairsSection = (JObject)extraSection["profitablePairs"]; + _profitablePairs = new List(); + int counter = 0; + foreach (var j in profitablePairsSection) + { + if (counter >= _systemConfiguration.GeneralSettings.Monitor.MaxTopMarkets) + { + break; + } + // Process each JObject in the dictionary + JObject profitablePair = (JObject)j.Value; + _profitablePairs.Add(BuildProfitablePairs(profitablePair)); + counter++; + } + _profitablePairsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + } + } + } + return _profitablePairs; + } + } + private ProfitablePairsData BuildProfitablePairs(JObject profitablePairsJson) + { + return new ProfitablePairsData() + { + Coin = profitablePairsJson["coin"].Value(), + ProfitCurrency = profitablePairsJson["profitCurrency"].Value(), + SoldTimes = profitablePairsJson["soldTimes"].Value(), + Avg = profitablePairsJson["avg"].Value(), + }; + } + + + public List DailyTCV @@ -493,7 +580,7 @@ namespace Core.Main.DataObjects this.BuildSellLogData(sellDataPage); pageIndex++; requestedPages++; - Console.WriteLine($"Importing sale: {pageIndex}"); +Console.WriteLine($"Importing sale: {pageIndex}"); } else diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index 6b5d34f..cc42f5c 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -219,8 +219,49 @@

Unable to load graph, no sales data found.

} + +
+
+
+

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

+ + + + + + + + + + + + + @{ + int rank = 1; + foreach (var pair in Model.ProfitablePairs) + { + string coin = pair.Coin; + double profit = Math.Round(pair.ProfitCurrency,8); + int sales = pair.SoldTimes; + double avg = Math.Round(pair.Avg,8); + double profitFiat = Math.Round(profit * Model.MiscData.FiatConversionRate, 0); + + + + + + + + + rank++; + } + } + +
RankMarketProfit @Model.PTData.Misc.MarketSalesAvg/TradeProfit @Model.PTData.Properties.Currency
@rank@coin@profit@sales@avg @profitFiat
+
+
@@ -315,55 +356,15 @@
-
-
-
-

Top @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets Sales Market Analysis

- - - - - - - - - - - - - - @{ - var topMarkets = Model.PTData.SellLog.GroupBy(m => m.Market).Select(mg => mg.Sum(m => m.Profit)); - int marketRank = 0; - } - @foreach (KeyValuePair marketData in Model.TopMarkets) { - marketRank++; - int trades = Model.PTData.SellLog.FindAll(m => m.Market == marketData.Key).Count; - double profitFiat = Math.Round(marketData.Value * Model.Summary.MainMarketPrice, 2); - double profitFiatPerTrade = Math.Round(profitFiat / trades, 2); - - - - - - - - - - } - -
RankMarketSalesProfit @Model.Summary.MainMarketProfit @Model.Summary.MainFiatCurrencyProfit @Model.Summary.MainFiatCurrency/TradeProfit %/Trade
@marketRank@marketData.Key@trades@marketData.Value.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))@profitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) @Model.Summary.MainFiatCurrency@profitFiatPerTrade.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) @Model.Summary.MainFiatCurrency@Model.PTData.SellLog.FindAll(m => m.Market == marketData.Key).Average(p => p.ProfitPercent).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
-
-
-
+ - *@ } @section Scripts { @@ -402,34 +403,6 @@ nv.utils.windowResize(lineChart.update); return lineChart; }); -@* - nv.addGraph(function () { - var lineChart = nv.models.lineChart(); - var height = 300; - /**/ - var chartData = @Html.Raw(Model.ProfitChartDataJSON); - /**/ - lineChart.useInteractiveGuideline(true); - lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); }); - lineChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f')); - d3.select('.profit-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart); - nv.utils.windowResize(lineChart.update); - return lineChart; - }); - - nv.addGraph(function () { - var lineChart = nv.models.lineChart(); - var height = 400; - /**/ - var chartData = @Html.Raw(Model.BalanceChartDataJSON); - /**/ - lineChart.useInteractiveGuideline(true); - lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); }); - lineChart.yAxis.axisLabel('Profit').tickFormat(d3.format(',.2f')); - d3.select('.balance-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart); - nv.utils.windowResize(lineChart.update); - return lineChart; - }); *@ $("#salesList").on("show.bs.modal", function (e) { $(this).find(".modal-content").html(''); @@ -481,6 +454,7 @@ d3.select('.nvtooltip').style('opacity', 0); } }); + nv.utils.windowResize(cumulativeProfitChart.update); return cumulativeProfitChart; }); @@ -528,6 +502,7 @@ d3.select('.nvtooltip').style('opacity', 0); } }); + nv.utils.windowResize(TCVChart.update); return TCVChart; }); @@ -575,6 +550,7 @@ d3.select('.nvtooltip').style('opacity', 0); } }); + nv.utils.windowResize(profitChart.update); return profitChart; }); diff --git a/Monitor/Pages/SalesAnalyzer.cshtml.cs b/Monitor/Pages/SalesAnalyzer.cshtml.cs index 89cb5da..6c56c89 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml.cs +++ b/Monitor/Pages/SalesAnalyzer.cshtml.cs @@ -16,6 +16,7 @@ namespace Monitor.Pages public StatsData StatsData { get; set; } public List DailyPNL { get; set; } public List DailyTCV { get; set; } + public List ProfitablePairs { get; set; } public int ProfitDays { get; set; } public int TCVDays { get; set; } public List MonthlyStats { get; set; } @@ -48,21 +49,16 @@ namespace Monitor.Pages MonthlyStats = this.PTData.MonthlyStats; DailyPNL = this.PTData.DailyPNL; DailyTCV = this.PTData.DailyTCV; + ProfitablePairs = this.PTData.ProfitablePairs; - //List monthlyStatsData = this.PTData.MonthlyStats; - //List dailyPNLData = this.PTData.DailyPNL; - - // Convert local offset time to UTC TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan); - BuildTopMarkets(); BuildSalesChartData(); BuildProfitChartData(); BuildCumulativeProfitChartData(); BuildTCVChartData(); - //MonthlyAverages(monthlyStatsData, PTData.Stats.FundingTotal); } private void BuildTCVChartData() { @@ -314,25 +310,14 @@ namespace Monitor.Pages int tradeDayIndex = 0; string tradesPerDayJSON = ""; - string profitPerDayJSON = ""; - string balancePerDayJSON = ""; - double balance = 0.0; for (DateTime salesDate = graphStartDate; salesDate <= DateTimeNow.DateTime.Date; salesDate = salesDate.AddDays(1)) { if (tradeDayIndex > 0) { tradesPerDayJSON += ",\n"; - profitPerDayJSON += ",\n"; - balancePerDayJSON += ",\n"; } - double profit = 0; int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Count; - profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit); - double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2); - balance += profitFiat; tradesPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + trades + "}"; - profitPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; - balancePerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + balance.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; tradeDayIndex++; } TradesChartDataJSON = "["; @@ -343,42 +328,7 @@ namespace Monitor.Pages TradesChartDataJSON += "}"; TradesChartDataJSON += "]"; - ProfitChartDataJSON = "["; - ProfitChartDataJSON += "{"; - ProfitChartDataJSON += "key: 'Profit in " + Summary.MainFiatCurrency + "',"; - ProfitChartDataJSON += "color: '" + Constants.ChartLineColors[1] + "',"; - ProfitChartDataJSON += "values: [" + profitPerDayJSON + "]"; - ProfitChartDataJSON += "}"; - ProfitChartDataJSON += "]"; - - BalanceChartDataJSON = "["; - BalanceChartDataJSON += "{"; - BalanceChartDataJSON += "key: 'Profit in " + Summary.MainFiatCurrency + "',"; - BalanceChartDataJSON += "color: '" + Constants.ChartLineColors[1] + "',"; - BalanceChartDataJSON += "values: [" + balancePerDayJSON + "]"; - BalanceChartDataJSON += "}"; - BalanceChartDataJSON += "]"; - for (DateTime salesDate = DateTimeNow.DateTime.Date; salesDate >= MinSellLogDate; salesDate = salesDate.AddDays(-1)) - { - List salesDateSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate); - double salesDateProfit; - salesDateProfit = salesDateSales.Sum(sl => sl.Profit); - double salesDateStartBalance = PTData.GetSnapshotBalance(salesDate); - double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2); - DailyGains.Add(salesDate, salesDateGain); - } - DateTime minSellLogMonthDate = new DateTime(MinSellLogDate.Year, MinSellLogDate.Month, 1).Date; - DateTime salesMonthStartDate = new DateTime(DateTimeNow.DateTime.Year, DateTimeNow.DateTime.Month, 1).Date; - for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) - { - List salesMonthSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year); - double salesDateProfit; - salesDateProfit = salesMonthSales.Sum(sl => sl.Profit); - double salesDateStartBalance = PTData.GetSnapshotBalance(salesMonthDate); - double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2); - MonthlyGains.Add(salesMonthDate, salesDateGain); - } } } } diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index 2e5fd8e..50531c3 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -51,7 +51,7 @@
@*
*@ -
+

Daily Profit @if (!Model.ProfitChartDataJSON.Equals("")) {
@@ -308,7 +308,7 @@ d3.select('.nvtooltip').style('opacity', 0); } }); - + nv.utils.windowResize(assetDistributionChart.update); return assetDistributionChart; }); @@ -355,7 +355,7 @@ d3.select('.nvtooltip').style('opacity', 0); } }); - + nv.utils.windowResize(trendChart.update); return trendChart; }); @@ -403,8 +403,7 @@ d3.select('.nvtooltip').style('opacity', 0); } }); - - + nv.utils.windowResize(profitChart.update); return profitChart; }); 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 09/14] 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 @@ + + \ No newline at end of file diff --git a/Monitor/Pages/_get/SalesList.cshtml.cs b/Monitor/Pages/_get/SalesList.cshtml.cs deleted file mode 100644 index 12ab483..0000000 --- a/Monitor/Pages/_get/SalesList.cshtml.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Http; -using Core.Main; -using Core.Helper; -using Core.Main.DataObjects; -using Core.Main.DataObjects.PTMagicData; -using Core.MarketAnalyzer; - -namespace Monitor.Pages { - public class SalesListModel : _Internal.BasePageModelSecure { - public ProfitTrailerData PTData = null; - private string salesDateString = ""; - private string salesMonthString = ""; - - public string SalesTimeframe = ""; - public DateTime SalesDate = Constants.confMinDate; - public List SellLog = new List(); - - public void OnGet() { - // Initialize Config - base.Init(); - - BindData(); - } - - private void BindData() { - salesDateString = GetStringParameter("d", ""); - salesMonthString = GetStringParameter("m", ""); - - PTData = this.PtDataObject; - - if (!salesDateString.Equals("")) { - SalesDate = SystemHelper.TextToDateTime(salesDateString, Constants.confMinDate); - if (SalesDate != Constants.confMinDate) { - SalesTimeframe = SalesDate.ToShortDateString(); - SellLog = PTData.SellLog.FindAll(sl => sl.SoldDate.Date == SalesDate.Date).OrderByDescending(sl => sl.SoldDate).ToList(); - } - } else if (!salesMonthString.Equals("")) { - SalesDate = SystemHelper.TextToDateTime(salesMonthString + "-01", Constants.confMinDate); - if (SalesDate != Constants.confMinDate) { - SalesTimeframe = SalesDate.ToString("MMMM yyyy", new System.Globalization.CultureInfo("en-US")); - SellLog = PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == SalesDate.Month && sl.SoldDate.Date.Year == SalesDate.Year).OrderByDescending(sl => sl.SoldDate).ToList(); - } - } - } - } -} diff --git a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json index 50a9d5d..650c85b 100644 --- a/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json +++ b/PTMagic/_defaults/_default_settings_PT_2.x/settings.general.json @@ -11,10 +11,8 @@ "ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings) "Exchange": "Bittrex", // The exchange your are running Profit Trailer on "TimezoneOffset": "+0:00", // Your timezone offset from UTC time - //"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them - "CoinMarketCapAPIKey": "", //CoinMarketCap Api //"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key }, "Monitor": { @@ -28,7 +26,6 @@ "ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days "RefreshSeconds": 30, // The refresh interval of your monitor main page "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it - "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart. "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer "MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer "MaxMonthlySummaries": 10, // The amount of "Last Months" being shown in your Sales Analyzer diff --git a/_Development/DevSettings/settings.general.json b/_Development/DevSettings/settings.general.json index 50a9d5d..9b8e822 100644 --- a/_Development/DevSettings/settings.general.json +++ b/_Development/DevSettings/settings.general.json @@ -11,11 +11,9 @@ "ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings) "Exchange": "Bittrex", // The exchange your are running Profit Trailer on "TimezoneOffset": "+0:00", // Your timezone offset from UTC time - //"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor - "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute + "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them "CoinMarketCapAPIKey": "", //CoinMarketCap Api - //"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key }, "Monitor": { "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start @@ -28,7 +26,6 @@ "ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days "RefreshSeconds": 30, // The refresh interval of your monitor main page "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it - "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart. "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer "MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer "MaxMonthlySummaries": 10, // The amount of "Last Months" being shown in your Sales Analyzer From 871818d88b7c104761bc3ab8cafce70c42a94f56 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Fri, 19 Jan 2024 18:43:04 +0900 Subject: [PATCH 12/14] version number --- PTMagic/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PTMagic/Program.cs b/PTMagic/Program.cs index c12762a..ca7ee6a 100644 --- a/PTMagic/Program.cs +++ b/PTMagic/Program.cs @@ -6,7 +6,7 @@ using Core.Helper; using Microsoft.Extensions.DependencyInjection; -[assembly: AssemblyVersion("2.6.1")] +[assembly: AssemblyVersion("2.7.1")] [assembly: AssemblyProduct("PT Magic")] namespace PTMagic From 44610b4d6e2ed748662d20e8f36d6416e7aa5de0 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Sun, 21 Jan 2024 13:22:10 +0900 Subject: [PATCH 13/14] Login & Transactions --- Monitor/Pages/Login.cshtml | 1 - Monitor/Pages/Login.cshtml.cs | 13 +------------ Monitor/Pages/_Layout.cshtml | 1 - Monitor/Startup.cs | 2 +- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/Monitor/Pages/Login.cshtml b/Monitor/Pages/Login.cshtml index ad74c3b..41d69fd 100644 --- a/Monitor/Pages/Login.cshtml +++ b/Monitor/Pages/Login.cshtml @@ -28,7 +28,6 @@
-

diff --git a/Monitor/Pages/Login.cshtml.cs b/Monitor/Pages/Login.cshtml.cs index a78502c..3996159 100644 --- a/Monitor/Pages/Login.cshtml.cs +++ b/Monitor/Pages/Login.cshtml.cs @@ -32,18 +32,7 @@ namespace Monitor.Pages HttpContext.Session.SetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(), DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")); PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = true; //PTMagicConfiguration.WriteGeneralSettings(); - if (cbRememberMe != null) - { - if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase)) - { - CookieOptions cookieOption = new CookieOptions(); - cookieOption.Expires = DateTime.UtcNow.AddYears(1); - - string cookieValue = EncryptionHelper.Encrypt(encryptedPassword); - - Response.Cookies.Append("PTMRememberMeKey", cookieValue, cookieOption); - } - } + Response.Redirect(PTMagicConfiguration.GeneralSettings.Monitor.RootUrl); } diff --git a/Monitor/Pages/_Layout.cshtml b/Monitor/Pages/_Layout.cshtml index 8096a29..337bab8 100644 --- a/Monitor/Pages/_Layout.cshtml +++ b/Monitor/Pages/_Layout.cshtml @@ -101,7 +101,6 @@ diff --git a/Monitor/Startup.cs b/Monitor/Startup.cs index fa46e14..6a62a6f 100644 --- a/Monitor/Startup.cs +++ b/Monitor/Startup.cs @@ -50,7 +50,7 @@ namespace Monitor services.AddDistributedMemoryCache(); services.AddSession(options => { - options.IdleTimeout = TimeSpan.FromSeconds(900); + options.IdleTimeout = TimeSpan.FromSeconds(1800); options.Cookie.HttpOnly = true; options.Cookie.Name = "PTMagicMonitor" + systemConfiguration.GeneralSettings.Monitor.Port.ToString(); }); From e474033d9c67d1206921d40c34f890e2a280319f Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Sun, 21 Jan 2024 15:59:24 +0900 Subject: [PATCH 14/14] Sales Chart --- Monitor/Pages/Login.cshtml.cs | 13 +- Monitor/Pages/MarketAnalyzer.cshtml | 2 +- Monitor/Pages/MarketAnalyzer.cshtml.cs | 138 ++++++++++--------- Monitor/Pages/SalesAnalyzer.cshtml | 6 +- Monitor/Pages/SalesAnalyzer.cshtml.cs | 21 ++- Monitor/Pages/_get/DashboardBottom.cshtml | 2 +- Monitor/Pages/_get/DashboardBottom.cshtml.cs | 1 - 7 files changed, 101 insertions(+), 82 deletions(-) diff --git a/Monitor/Pages/Login.cshtml.cs b/Monitor/Pages/Login.cshtml.cs index 3996159..a78502c 100644 --- a/Monitor/Pages/Login.cshtml.cs +++ b/Monitor/Pages/Login.cshtml.cs @@ -32,7 +32,18 @@ namespace Monitor.Pages HttpContext.Session.SetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(), DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")); PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = true; //PTMagicConfiguration.WriteGeneralSettings(); - + if (cbRememberMe != null) + { + if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase)) + { + CookieOptions cookieOption = new CookieOptions(); + cookieOption.Expires = DateTime.UtcNow.AddYears(1); + + string cookieValue = EncryptionHelper.Encrypt(encryptedPassword); + + Response.Cookies.Append("PTMRememberMeKey", cookieValue, cookieOption); + } + } Response.Redirect(PTMagicConfiguration.GeneralSettings.Monitor.RootUrl); } diff --git a/Monitor/Pages/MarketAnalyzer.cshtml b/Monitor/Pages/MarketAnalyzer.cshtml index 13a2f25..304212f 100644 --- a/Monitor/Pages/MarketAnalyzer.cshtml +++ b/Monitor/Pages/MarketAnalyzer.cshtml @@ -155,7 +155,7 @@ else
-

Live Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange

+

Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange

diff --git a/Monitor/Pages/MarketAnalyzer.cshtml.cs b/Monitor/Pages/MarketAnalyzer.cshtml.cs index 9d27e60..be4faf6 100644 --- a/Monitor/Pages/MarketAnalyzer.cshtml.cs +++ b/Monitor/Pages/MarketAnalyzer.cshtml.cs @@ -4,6 +4,7 @@ using System.Linq; using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; +using System.Globalization; namespace Monitor.Pages { @@ -11,6 +12,7 @@ namespace Monitor.Pages { public List MarketTrends { get; set; } = new List(); public string TrendChartDataJSON = ""; + public double DataHours { get; set; } public void OnGet() { @@ -28,77 +30,85 @@ namespace Monitor.Pages private void BuildMarketTrendChartData() { - if (MarketTrends.Count > 0) - { - TrendChartDataJSON = "["; - int mtIndex = 0; - foreach (MarketTrend mt in MarketTrends) + List trendChartData = new List(); + if (MarketTrends.Count > 0) { - if (mt.DisplayGraph) - { - string lineColor = ""; - if (mtIndex < Constants.ChartLineColors.Length) + + int mtIndex = 0; + foreach (MarketTrend mt in MarketTrends) { - lineColor = Constants.ChartLineColors[mtIndex]; - } - else - { - lineColor = Constants.ChartLineColors[mtIndex - 20]; - } - - if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) - { - List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; - - if (marketTrendChangeSummaries.Count > 0) - { - if (!TrendChartDataJSON.Equals("[")) TrendChartDataJSON += ","; - - TrendChartDataJSON += "{"; - TrendChartDataJSON += "key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',"; - TrendChartDataJSON += "color: '" + lineColor + "',"; - TrendChartDataJSON += "values: ["; - - // Get trend ticks for chart - DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); - DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); - DateTime endDateTime = currentDateTime; - int trendChartTicks = 0; - for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + if (mt.DisplayGraph) { - List tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); - if (tickRange.Count > 0) - { - MarketTrendChange mtc = tickRange.First(); - if (tickTime != startDateTime) TrendChartDataJSON += ",\n"; - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; + string lineColor = mtIndex < Constants.ChartLineColors.Length + ? Constants.ChartLineColors[mtIndex] + : Constants.ChartLineColors[mtIndex - 20]; - TrendChartDataJSON += "{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; - trendChartTicks++; - } + if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) + { + List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; + + if (marketTrendChangeSummaries.Count > 0) + { + List trendValues = new List(); + + // Sort marketTrendChangeSummaries by TrendDateTime + marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList(); + + // Get trend ticks for chart + TimeSpan offset; + bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-"); + string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-'); + + if (!TimeSpan.TryParse(offsetWithoutSign, out offset)) + { + offset = TimeSpan.Zero; // If offset is invalid, set it to zero + } + + DateTime currentDateTime = DateTime.UtcNow; + DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); + DateTime endDateTime = currentDateTime; + + // Ensure startDateTime doesn't exceed the available data + DateTime earliestTrendDateTime = marketTrendChangeSummaries.Min(mtc => mtc.TrendDateTime); + startDateTime = startDateTime > earliestTrendDateTime ? startDateTime : earliestTrendDateTime; + DataHours = (currentDateTime - earliestTrendDateTime).TotalHours; + + // Cache the result of SplitCamelCase(mt.Name) + string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name); + + for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + { + // Use binary search to find the range of items that match the condition + int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer.Create((x, y) => x.TrendDateTime.CompareTo(y.TrendDateTime))); + if (index < 0) index = ~index; + if (index < marketTrendChangeSummaries.Count) + { + MarketTrendChange mtc = marketTrendChangeSummaries[index]; + if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; + + // Adjust tickTime to the desired timezone before converting to string + DateTime adjustedTickTime = tickTime.Add(isNegative ? -offset : offset); + trendValues.Add("{ x: new Date('" + adjustedTickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + } + } + + // Add most recent tick + MarketTrendChange latestMtc = marketTrendChangeSummaries.Last(); + if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0; + + // Adjust latestMtc.TrendDateTime to the desired timezone before converting to string + DateTime adjustedLatestTrendDateTime = latestMtc.TrendDateTime.Add(isNegative ? -offset : offset); + trendValues.Add("{ x: new Date('" + adjustedLatestTrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}"); + + // Use cached splitCamelCaseName + trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }"); + mtIndex++; + } + } } - - // Add most recent tick - List latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); - if (latestTickRange.Count > 0) - { - MarketTrendChange mtc = latestTickRange.First(); - if (trendChartTicks > 0) TrendChartDataJSON += ",\n"; - if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; - - TrendChartDataJSON += "{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; - } - - TrendChartDataJSON += "]"; - TrendChartDataJSON += "}"; - - mtIndex++; - } } - } } - TrendChartDataJSON += "]"; - } + TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]"; } } } diff --git a/Monitor/Pages/SalesAnalyzer.cshtml b/Monitor/Pages/SalesAnalyzer.cshtml index 0496bc6..459f086 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml +++ b/Monitor/Pages/SalesAnalyzer.cshtml @@ -379,9 +379,11 @@ @if (!Model.SalesChartDataJSON.Equals("")) { nv.addGraph(function () { - salesChart = nv.models.lineChart(); + salesChart = nv.models.multiBarChart(); var height = 300; - salesChart.useInteractiveGuideline(true); + salesChart.groupSpacing(0.5); + salesChart.showControls(false); + //salesChart.useInteractiveGuideline(true); salesChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); }); salesChart.yAxis.axisLabel('').tickFormat(d3.format(',.2f')); diff --git a/Monitor/Pages/SalesAnalyzer.cshtml.cs b/Monitor/Pages/SalesAnalyzer.cshtml.cs index 0f58ca4..dbe7c92 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml.cs +++ b/Monitor/Pages/SalesAnalyzer.cshtml.cs @@ -10,7 +10,7 @@ using Core.Main.DataObjects.PTMagicData; namespace Monitor.Pages { public class SalesAnalyzer : _Internal.BasePageModelSecure - { + { public ProfitTrailerData PTData = null; public MiscData MiscData { get; set; } public PropertiesData PropertiesData { get; set; } @@ -328,28 +328,25 @@ namespace Monitor.Pages { if (salesCountByDate.TryGetValue(date, out DailyStatsData dailyStatsData)) { - // Use the totalSales value directly - salesPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = dailyStatsData.TotalSales }); - - // Add daily buys to the list - buysPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = dailyStatsData.TotalBuys }); + buysPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = Convert.ToInt32(dailyStatsData.TotalBuys) }); + salesPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = Convert.ToInt32(dailyStatsData.TotalSales) }); } } // Convert the lists to a JSON string using Newtonsoft.Json SalesChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] { + new { // New JSON object for daily buys + key = "Buys", + color = Constants.ChartLineColors[19], // Use a different color for buys + values = buysPerDayList + }, new { key = "Sales", color = Constants.ChartLineColors[1], values = salesPerDayList - }, - new { // New JSON object for daily buys - key = "Buys", - color = Constants.ChartLineColors[0], // Use a different color for buys - values = buysPerDayList } }); } - } + } } } diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index 1f86804..2e09d17 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -12,7 +12,7 @@
-

Market Trend History

+

Market Trend History

@if (!Model.TrendChartDataJSON.Equals("")) {
diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index 970e0bd..6685342 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -8,7 +8,6 @@ using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; using System.Globalization; using System.Text; -using System.Diagnostics; namespace Monitor.Pages {