More Dashboard Bottom Optimizations

This commit is contained in:
HojouFotytu 2024-01-11 00:58:09 +09:00
parent 28c12e6d5a
commit 16fada1443
12 changed files with 263 additions and 169 deletions

View File

@ -41,10 +41,8 @@ namespace Core.Main.DataObjects.PTMagicData
public string ProfitTrailerDefaultSettingName { get; set; } = "default"; public string ProfitTrailerDefaultSettingName { get; set; } = "default";
public int FloodProtectionMinutes { get; set; } = 15; public int FloodProtectionMinutes { get; set; } = 15;
public string Exchange { get; set; } public string Exchange { get; set; }
//public double StartBalance { get; set; } = 0;
public string InstanceName { get; set; } = "PT Magic"; public string InstanceName { get; set; } = "PT Magic";
public string TimezoneOffset { get; set; } = "+0:00"; public string TimezoneOffset { get; set; } = "+0:00";
public string MainFiatCurrency { get; set; } = "USD";
public string CoinMarketCapAPIKey { get; set; } public string CoinMarketCapAPIKey { get; set; }
//public string FreeCurrencyConverterAPIKey { get; set; } //public string FreeCurrencyConverterAPIKey { get; set; }
} }
@ -59,6 +57,7 @@ namespace Core.Main.DataObjects.PTMagicData
public string AnalyzerChart { get; set; } = ""; public string AnalyzerChart { get; set; } = "";
public int GraphIntervalMinutes { get; set; } = 60; public int GraphIntervalMinutes { get; set; } = 60;
public int GraphMaxTimeframeHours { get; set; } = 24; public int GraphMaxTimeframeHours { get; set; } = 24;
public int ProfitsMaxTimeframeDays { get; set; } = 60;
public int RefreshSeconds { get; set; } = 30; public int RefreshSeconds { get; set; } = 30;
public int BagAnalyzerRefreshSeconds { get; set; } = 5; public int BagAnalyzerRefreshSeconds { get; set; } = 5;
public int BuyAnalyzerRefreshSeconds { get; set; } = 5; public int BuyAnalyzerRefreshSeconds { get; set; } = 5;
@ -127,6 +126,7 @@ namespace Core.Main.DataObjects.PTMagicData
[DefaultValue("Market")] [DefaultValue("Market")]
public string TrendCurrency { get; set; } = "Market"; public string TrendCurrency { get; set; } = "Market";
public string SplitCamelCaseName { get; set; }
[DefaultValue(0)] [DefaultValue(0)]
public int MaxMarkets { get; set; } = 0; public int MaxMarkets { get; set; } = 0;
@ -442,15 +442,6 @@ namespace Core.Main.DataObjects.PTMagicData
public double FundingTotal { 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 class DailyPNLData
{ {
public string Date { get; set; } public string Date { get; set; }

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Diagnostics; using System.Diagnostics;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
@ -17,15 +18,25 @@ namespace Core.Main.DataObjects
private SummaryData _summary = null; private SummaryData _summary = null;
private PropertiesData _properties = null; private PropertiesData _properties = null;
private StatsData _stats = null; private StatsData _stats = null;
private List<DailyStatsData> _dailyStats = new List<DailyStatsData>();
private List<DailyPNLData> _dailyPNL = new List<DailyPNLData>(); private List<DailyPNLData> _dailyPNL = new List<DailyPNLData>();
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<SellLogData> _sellLog = new List<SellLogData>(); private List<SellLogData> _sellLog = new List<SellLogData>();
private List<DCALogData> _dcaLog = new List<DCALogData>(); private List<DCALogData> _dcaLog = new List<DCALogData>();
private List<BuyLogData> _buyLog = new List<BuyLogData>(); private List<BuyLogData> _buyLog = new List<BuyLogData>();
private string _ptmBasePath = ""; private string _ptmBasePath = "";
private PTMagicConfiguration _systemConfiguration = null; private PTMagicConfiguration _systemConfiguration = null;
private TransactionData _transactionData = null; private TransactionData _transactionData = null;
private DateTime _dailyStatsRefresh = DateTime.UtcNow;
private DateTime _dailyPNLRefresh = DateTime.UtcNow; private DateTime _dailyPNLRefresh = DateTime.UtcNow;
private DateTime _statsRefresh = DateTime.UtcNow; private DateTime _statsRefresh = DateTime.UtcNow;
private DateTime _buyLogRefresh = DateTime.UtcNow; private DateTime _buyLogRefresh = DateTime.UtcNow;
@ -143,6 +154,28 @@ namespace Core.Main.DataObjects
BaseUrl = PTProperties.baseUrl 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 public StatsData Stats
{ {
get get
@ -153,17 +186,29 @@ namespace Core.Main.DataObjects
{ {
if (_stats == null || DateTime.UtcNow > _statsRefresh) if (_stats == null || DateTime.UtcNow > _statsRefresh)
{ {
dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats"))
JObject statsDataJObject = statsDataJson as JObject; using (var reader = new StreamReader(stream))
JObject basicSection = (JObject)statsDataJObject["basic"]; 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); _stats = BuildStatsData(basicSection);
_statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
break;
}
}
}
} }
} }
} }
return _stats; return _stats;
} }
} }
private StatsData BuildStatsData(dynamic statsDataJson) private StatsData BuildStatsData(dynamic statsDataJson)
{ {
return new StatsData() return new StatsData()
@ -194,38 +239,28 @@ namespace Core.Main.DataObjects
FundingTotal = statsDataJson["totalFunding"] FundingTotal = statsDataJson["totalFunding"]
}; };
} }
public List<DailyStatsData> DailyStats // public List<DailyPNLData> DailyPNL
{ // {
get // get
{ // {
if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) // if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
{ // {
lock (_dailyStatsLock) // lock (_dailyPNLLock)
{ // {
if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh) // if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
{ // {
dynamic dailyStatsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); // dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats");
JObject dailyStatsDataJObject = dailyStatsDataJson as JObject; // JObject dailyPNLDataJObject = dailyPNLDataJson as JObject;
JArray dailyStatsSection = (JArray)dailyStatsDataJObject["extra"]["dailyStats"]; // JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"];
_dailyStats = dailyStatsSection.Select(j => BuildDailyStatsData(j as JObject)).ToList(); // _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList();
_dailyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); // _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
} // }
} // }
} // }
return _dailyStats; // return _dailyPNL;
} // }
} // }
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<DailyPNLData> DailyPNL public List<DailyPNLData> DailyPNL
{ {
get get
@ -236,14 +271,55 @@ namespace Core.Main.DataObjects
{ {
if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh) if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
{ {
dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats"); using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats"))
JObject dailyPNLDataJObject = dailyPNLDataJson as JObject; using (var reader = new StreamReader(stream))
JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"]; 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<decimal>())) ||
(_totalSales == null ||
!Decimal.Equals(_totalSales.Value, basicSection["totalSales"].Value<decimal>()))))
{
_totalProfit = basicSection["totalProfit"].Value<decimal>();
_totalSales = basicSection["totalSales"].Value<decimal>();
if (extraSection != null)
{
JArray dailyPNLSection = (JArray)extraSection["dailyPNLStats"];
_dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList(); _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList();
_dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
} }
} }
} }
}
}
}
return _dailyPNL; return _dailyPNL;
} }
} }
@ -455,6 +531,23 @@ namespace Core.Main.DataObjects
return JArray.Parse(rawBody); 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) private void BuildSellLogData(dynamic rawSellLogData)

View File

@ -14,7 +14,6 @@ namespace Core.Main
{ {
public class PTMagic public class PTMagic
{ {
public PTMagic(LogHelper log) public PTMagic(LogHelper log)
{ {
this.Log = log; this.Log = log;
@ -84,7 +83,10 @@ namespace Core.Main
_systemConfiguration = value; _systemConfiguration = value;
} }
} }
public class IsAnalzyerRunning
{
private string _isAnalyzerRunning;
}
public System.Timers.Timer Timer public System.Timers.Timer Timer
{ {
get 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"); 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) catch (System.NullReferenceException)
{ {
@ -871,9 +864,6 @@ namespace Core.Main
// Check for latest GitHub version // Check for latest GitHub version
this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version); this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version);
// Get latest main fiat currency exchange rate
// this.GetMainFiatCurrencyDetails();
// Load current PT files // Load current PT files
this.LoadCurrentProfitTrailerProperties(); this.LoadCurrentProfitTrailerProperties();

View File

@ -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'")); 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.GeneralSettings.Monitor.IsPasswordProtected = true;
PTMagicConfiguration.WriteGeneralSettings(); //PTMagicConfiguration.WriteGeneralSettings();
if (cbRememberMe != null) if (cbRememberMe != null)
{ {
if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase)) if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase))

View File

@ -37,7 +37,7 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 header-title text-center">Cumulative Profits <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This value is based on your sales history"></i></small></h4> <h4 class="m-t-0 header-title text-center">Cumulative PNL <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This value is based on your sales history"></i></small></h4>
<div class="balance-chart"> <div class="balance-chart">
<svg style="height:350px;width:100%"></svg> <svg style="height:350px;width:100%"></svg>
</div> </div>

View File

@ -203,12 +203,19 @@
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">Market Trend Graph Max Timeframe <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the market trends graph."></i></label> <label class="col-md-4 col-form-label">Market Trend Graph Max Timeframe <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the market trends graph in hours."></i></label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphMaxTimeframeHours" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours.ToString(new System.Globalization.CultureInfo("en-US"))"> <input type="text" class="form-control" name="Monitor_GraphMaxTimeframeHours" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours.ToString(new System.Globalization.CultureInfo("en-US"))">
</div> </div>
</div> </div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Daily Profit Graph Max Timeframe <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the daily profits graph on the dashboard in days."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_ProfitsMaxTimeframeDays" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">Refresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor main page."></i></label> <label class="col-md-4 col-form-label">Refresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor main page."></i></label>
<div class="col-md-8"> <div class="col-md-8">

View File

@ -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.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.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.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.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.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.BuyAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BuyAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds);

View File

@ -82,7 +82,7 @@
<tbody> <tbody>
@foreach (var marketTrend in Model.MarketTrends.OrderBy(mt => mt.TrendMinutes)) { @foreach (var marketTrend in Model.MarketTrends.OrderBy(mt => mt.TrendMinutes)) {
if (Model.Summary.MarketTrendChanges.ContainsKey(marketTrend.Name)) { if (Model.Summary.MarketTrendChanges.ContainsKey(marketTrend.Name)) {
double trendChange = Model.Summary.MarketTrendChanges[marketTrend.Name].OrderByDescending(mtc => mtc.TrendDateTime).First().TrendChange; double trendChange = Model.Summary.MarketTrendChanges[marketTrend.Name].Last().TrendChange;
string trendChangeOutput = trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); string trendChangeOutput = trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
int marketCount = marketTrend.MaxMarkets; int marketCount = marketTrend.MaxMarkets;
@ -93,8 +93,12 @@
} else if (marketCount > Model.Summary.MarketSummary.Keys.Count && marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) { } else if (marketCount > Model.Summary.MarketSummary.Keys.Count && marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) {
marketCountString = Model.Summary.MarketSummary.Keys.Count.ToString(); marketCountString = Model.Summary.MarketSummary.Keys.Count.ToString();
} }
// Cache the result of SplitCamelCase(marketTrend.Name)
string splitCamelCaseName = Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name);
<tr> <tr>
<td>@Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name)</td> <td>@splitCamelCaseName</td> <!-- Use the cached value here -->
<td class="text-right">@marketCountString</td> <td class="text-right">@marketCountString</td>
<td class="text-right">@Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false)</td> <td class="text-right">@Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false)</td>
@if (marketTrend.TrendThreshold == 0) @if (marketTrend.TrendThreshold == 0)

View File

@ -17,8 +17,7 @@ namespace Monitor.Pages
public StatsData StatsData { get; set; } public StatsData StatsData { get; set; }
public PropertiesData PropertiesData { get; set; } public PropertiesData PropertiesData { get; set; }
public SummaryData SummaryData { get; set; } public SummaryData SummaryData { get; set; }
public List<DailyStatsData> DailyStats { get; set; } = new List<DailyStatsData>();
public List<DailyPNLData> DailyPNL { get; set; } = new List<DailyPNLData>();
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>(); public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
public string TrendChartDataJSON = ""; public string TrendChartDataJSON = "";
public string ProfitChartDataJSON = ""; public string ProfitChartDataJSON = "";
@ -42,7 +41,7 @@ namespace Monitor.Pages
StatsData = this.PTData.Stats; StatsData = this.PTData.Stats;
PropertiesData = this.PTData.Properties; PropertiesData = this.PTData.Properties;
SummaryData = this.PTData.Summary; SummaryData = this.PTData.Summary;
List<DailyStatsData> dailyStatsData = this.PTData.DailyStats;
List<DailyPNLData> dailyPNLData = this.PTData.DailyPNL; List<DailyPNLData> dailyPNLData = this.PTData.DailyPNL;
// Cleanup temp files // Cleanup temp files
@ -67,24 +66,18 @@ namespace Monitor.Pages
} }
private void BuildMarketTrendChartData() private void BuildMarketTrendChartData()
{ {
StringBuilder trendChartDataJSON = new StringBuilder(); List<string> trendChartData = new List<string>();
if (MarketTrends.Count > 0) if (MarketTrends.Count > 0)
{ {
trendChartDataJSON.Append("[");
int mtIndex = 0; int mtIndex = 0;
foreach (MarketTrend mt in MarketTrends) foreach (MarketTrend mt in MarketTrends)
{ {
if (mt.DisplayGraph) if (mt.DisplayGraph)
{ {
string lineColor = ""; string lineColor = mtIndex < Constants.ChartLineColors.Length
if (mtIndex < Constants.ChartLineColors.Length) ? Constants.ChartLineColors[mtIndex]
{ : Constants.ChartLineColors[mtIndex - 20];
lineColor = Constants.ChartLineColors[mtIndex];
}
else
{
lineColor = Constants.ChartLineColors[mtIndex - 20];
}
if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) if (Summary.MarketTrendChanges.ContainsKey(mt.Name))
{ {
@ -92,71 +85,84 @@ namespace Monitor.Pages
if (marketTrendChangeSummaries.Count > 0) if (marketTrendChangeSummaries.Count > 0)
{ {
if (!trendChartDataJSON.ToString().Equals("[")) trendChartDataJSON.Append(","); List<string> trendValues = new List<string>();
trendChartDataJSON.Append("{"); // Sort marketTrendChangeSummaries by TrendDateTime
trendChartDataJSON.Append("key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',"); marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList();
trendChartDataJSON.Append("color: '" + lineColor + "',");
trendChartDataJSON.Append("values: [");
// Get trend ticks for chart // Get trend ticks for chart
DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); 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 startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
DateTime endDateTime = currentDateTime; DateTime endDateTime = currentDateTime;
int trendChartTicks = 0;
// 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)) for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes))
{ {
List<MarketTrendChange> tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); // Use binary search to find the range of items that match the condition
if (tickRange.Count > 0) int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer<MarketTrendChange>.Create((x, y) => x.TrendDateTime.CompareTo(y.TrendDateTime)));
if (index < 0) index = ~index;
if (index < marketTrendChangeSummaries.Count)
{ {
MarketTrendChange mtc = tickRange.First(); MarketTrendChange mtc = marketTrendChangeSummaries[index];
if (tickTime != startDateTime) trendChartDataJSON.Append(",\n");
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; 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")) + "}"); trendValues.Add("{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
trendChartTicks++;
} }
} }
// Add most recent tick // Add most recent tick
List<MarketTrendChange> latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); MarketTrendChange latestMtc = marketTrendChangeSummaries.Last();
if (latestTickRange.Count > 0) 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) + "}");
MarketTrendChange mtc = latestTickRange.First();
if (trendChartTicks > 0) trendChartDataJSON.Append(",\n"); // Use cached splitCamelCaseName
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }");
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++; mtIndex++;
} }
} }
} }
} }
trendChartDataJSON.Append("]");
} }
TrendChartDataJSON = trendChartDataJSON.ToString(); TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]";
} }
private void BuildProfitChartData() private void BuildProfitChartData()
{ {
StringBuilder profitPerDayJSON = new StringBuilder(); StringBuilder profitPerDayJSON = new StringBuilder();
if (PTData.DailyStats.Count > 0) if (PTData.DailyPNL.Count > 0)
{ {
DateTime endDate = DateTime.UtcNow.Date; 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)) 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) if (profitPerDayJSON.Length > 0)
{ {
profitPerDayJSON.Append(",\n"); 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")) + "}"); 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() + "]}]"; ProfitChartDataJSON = "[{key: 'Profit in " + PTData.Properties.Currency + "',color: '" + Constants.ChartLineColors[1] + "',values: [" + profitPerDayJSON.ToString() + "]}]";
} }
} }

View File

@ -23,8 +23,9 @@
"Port": 8080, // The port you want to run your monitor on "Port": 8080, // The port you want to run your monitor on
"RootUrl": "/", // The root Url of your monitor "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) "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 "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 "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 "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 "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. "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart.

View File

@ -23,8 +23,9 @@
"Port": 8080, // The port you want to run your monitor on "Port": 8080, // The port you want to run your monitor on
"RootUrl": "/", // The root Url of your monitor "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) "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 "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 "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 "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 "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. "MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart.

View File

@ -16,7 +16,7 @@
- This version of PT Magic only works with Profit Trailer v2.4.x and above - This version of PT Magic only works with Profit Trailer v2.4.x and above
### .Net ### .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 ### 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. - **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.