More Dashboard bottom optimizations

This commit is contained in:
HojouFotytu 2024-01-14 04:10:44 +09:00
parent 16fada1443
commit 08554da6ee
5 changed files with 290 additions and 165 deletions

View File

@ -33,11 +33,6 @@
$("#baglist-refresh-icon").html('<i class="fa fa-circle-o-notch fa-spin fa-fw" data-toggle="tooltip" data-placement="top" title="Loading fresh data..."></i>');
$("#buylist-refresh-icon").html('<i class="fa fa-circle-o-notch fa-spin fa-fw" data-toggle="tooltip" data-placement="top" title="Loading fresh data..."></i>');
// 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)

View File

@ -196,21 +196,21 @@
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Market Trend Graph Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The time interval for the market trend graph between points."></i></label>
<label class="col-md-4 col-form-label">Market Trend Graph Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The time interval for the market trend graph (Dashboard and Sales Analyzer) between data points. Very small intervals on large timeframe graphs can significantly impact performance."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphIntervalMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<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 in hours."></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 (Dashboard and Sales Analyzer) in hours. Large timeframe graphs can significantly impact performance."></i></label>
<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"))">
</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>
<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. Large timeframes can significantly impact performance."></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>

View File

@ -10,7 +10,8 @@
}
<div class="row">
<div class="col-md-5">
<div class="col-md-6">
<div class="card-box">
<h4 class="m-t-0 header-title">PTMagic Status <small id="last-refresh" class="pull-right"></small></h4>
@{
@ -71,7 +72,6 @@
@{
string maxCostCaption = "Initial";
}
<table class="table table-striped table-sm">
<tbody>
<tr>
@ -99,35 +99,32 @@
</table>
</div>
</div>
<div class="col-md-7">
<div class="row">
<div class="col-md-6">
<div class="card-box">
<h4 class="m-t-0 header-title">Settings Active Time (Last 24h)</h4>
<div id="gsChart24h">
<svg style="height:300px;width:100%"></svg>
<svg style="height:200px;width:100%"></svg>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card-box">
<h4 class="m-t-0 header-title">Settings Active Time (Last 3 days)</h4>
<div id="gsChart3d">
<svg style="height:300px;width:100%"></svg>
<svg style="height:200px;width:100%"></svg>
</div>
</div>
</div>
</div>
<div class="card-box">
</div>
<div class="card-box">
<h4 class="m-t-0 header-title">Global Settings Log</h4>
<table class="table table-striped table-sm">
<thead>
<tr>
@ -159,9 +156,6 @@
}
</tbody>
</table>
</div>
</div>
</div>

View File

@ -11,8 +11,8 @@
<div class="row">
<div class="col-md-5 px-1">
<div class="card-box px-2" style="height:305px;">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
<div class="card-box px-2" style="height:320px;">
<h4 class="m-t-0 m-b-20 header-title" style="display: inline;"><b>Market Trend History </b><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="@Math.Round(Model.DataHours, 1) hours of data available. Currently set to show @Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours hours in general settings."></i></h4>
@if (!Model.TrendChartDataJSON.Equals("")) {
<div class="trend-chart">
<svg style="height: 300px;width: 100%;"></svg>
@ -22,9 +22,10 @@
}
</div>
</div>
<div class="col-md-3 px-1">
<div class="card-box px-3" style="height:305px;">
@*<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>*@
<div class="card-box px-3" style="height:320px;">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="You dashboard is set to refresh every @Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds seconds in general settings."></i></div>
@{
string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"));
if (Model.totalCurrentValue > 100) {
@ -50,7 +51,8 @@
<div class="col-md-4 px-1">
@*<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>*@
<div class="card-box px-2" style="height:305px;">
<div class="card-box px-2" style="height:320px;">
<h4 class="m-t-0 m-b-20 header-title" style="display: inline;"><b>Daily Profit </b><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="@Model.ProfitDays days of data available. Currently Set to @Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays days in general settings."></i>
@if (!Model.ProfitChartDataJSON.Equals("")) {
<div class="profit-chart">
<svg style="height:300px;width:100%"></svg>
@ -65,8 +67,8 @@
<div class="row">
<div class="col-md-5 px-1">
<div class="card-box px-2">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
<br>
@* <div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
<br> *@
<h4 class="m-t-0 m-b-20 header-title"><b>Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange</b>
<small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">more</a></small></h4>
<table class="table table-sm">
@ -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;
});
</text>
@ -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;
});
</text>
@ -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;
});
</text>
}
})(jQuery);
</script>
<script type="text/javascript">
$(document).ready(function(){
var originalLeave = $.fn.tooltip.Constructor.prototype.leave;
$.fn.tooltip.Constructor.prototype.leave = function(obj){
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
var container, timeout;
originalLeave.call(this, obj);
if(obj.currentTarget) {
container = $(obj.currentTarget).siblings('.tooltip');
timeout = self.timeout;
container.one('mouseenter', function(){
//We entered the actual tooltip call off the dogs
clearTimeout(timeout);
//Let's monitor tooltip content instead
container.one('mouseleave', function(){
$.fn.tooltip.Constructor.prototype.leave.call(self, self);
});
});
}
};
$('[data-toggle="tooltip"]').tooltip();
});
</script>

View File

@ -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<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
public double DataHours { get; set; }
public int ProfitDays { get; set; }
public string TrendChartDataJSON = "";
public string ProfitChartDataJSON = "";
public string LastGlobalSetting = "Default";
@ -32,7 +35,6 @@ namespace Monitor.Pages
BindData();
BuildAssetDistributionData();
}
private void BindData()
@ -62,10 +64,11 @@ namespace Monitor.Pages
MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList();
BuildMarketTrendChartData();
BuildAssetDistributionData();
BuildProfitChartData();
}
private void BuildMarketTrendChartData()
{
{
List<string> trendChartData = new List<string>();
if (MarketTrends.Count > 0)
{
@ -91,10 +94,24 @@ namespace Monitor.Pages
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);
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);
@ -108,14 +125,19 @@ namespace Monitor.Pages
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) + "}");
// 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;
trendValues.Add("{ x: new Date('" + latestMtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
// 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) + "] }");
@ -124,25 +146,56 @@ namespace Monitor.Pages
}
}
}
}
TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]";
}
}
private void BuildProfitChartData()
{
StringBuilder profitPerDayJSON = new StringBuilder();
{
List<object> profitPerDayList = new List<object>();
if (PTData.DailyPNL.Count > 0)
{
DateTime endDate = DateTime.UtcNow.Date;
// 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<DateTime, DailyPNLData> 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))
{
DailyPNLData dailyPNL = PTData.DailyPNL.Find(ds => DateTime.ParseExact(ds.Date, "d-M-yyyy", CultureInfo.InvariantCulture) == date);
if (dailyPNL != null)
// Use the dictionary to find the DailyPNLData for the date
if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL))
{
if (isFirstDay)
{
@ -153,19 +206,22 @@ namespace Monitor.Pages
// 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")) + "}");
// Add the data point to the list
profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat });
}
previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency;
}
}
ProfitChartDataJSON = "[{key: 'Profit in " + PTData.Properties.Currency + "',color: '" + Constants.ChartLineColors[1] + "',values: [" + profitPerDayJSON.ToString() + "]}]";
// 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()
{
@ -180,7 +236,6 @@ 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);
// Aggregate totals
double leverage = dcaLogEntry.Leverage;
if (leverage == 0)