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.