More Dashboard bottom optimizations
This commit is contained in:
parent
16fada1443
commit
08554da6ee
|
@ -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>');
|
$("#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>');
|
$("#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.
|
// Clear exisitng interval to stop multiple attempts to load at the same time.
|
||||||
if (intervalDashboardTop != null)
|
if (intervalDashboardTop != null)
|
||||||
{
|
{
|
||||||
|
@ -69,13 +64,16 @@
|
||||||
|
|
||||||
var loadDashboardBottom = function () {
|
var loadDashboardBottom = function () {
|
||||||
//destroy all d3 svg graph to avoid memory leak
|
//destroy all d3 svg graph to avoid memory leak
|
||||||
//$(".nvtooltip").remove();
|
setTimeout(function()
|
||||||
//$("svg > *").remove();
|
{
|
||||||
//$("svg").remove();
|
$(".nvtooltip").remove();
|
||||||
//nv.charts = {};
|
$("svg > *").remove();
|
||||||
//nv.graphs = [];
|
$("svg").remove();
|
||||||
//nv.logs = {};
|
nv.charts = {};
|
||||||
//nv.tooltip = {};
|
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.
|
// Clear exisitng interval to stop multiple attempts to load at the same time.
|
||||||
if (intervalDashboardBottom != null)
|
if (intervalDashboardBottom != null)
|
||||||
|
|
|
@ -196,21 +196,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<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">
|
<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"))">
|
<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>
|
</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 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">
|
<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">
|
<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">
|
<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"))">
|
<input type="text" class="form-control" name="Monitor_ProfitsMaxTimeframeDays" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-5">
|
|
||||||
|
<div class="col-md-6">
|
||||||
<div class="card-box">
|
<div class="card-box">
|
||||||
<h4 class="m-t-0 header-title">PTMagic Status <small id="last-refresh" class="pull-right"></small></h4>
|
<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";
|
string maxCostCaption = "Initial";
|
||||||
}
|
}
|
||||||
|
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -99,69 +99,63 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</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>
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<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:200px;width:100%"></svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="card-box">
|
<div class="card-box">
|
||||||
<h4 class="m-t-0 header-title">Global Settings Log</h4>
|
<h4 class="m-t-0 header-title">Settings Active Time (Last 3 days)</h4>
|
||||||
|
<div id="gsChart3d">
|
||||||
<table class="table table-striped table-sm">
|
<svg style="height:200px;width:100%"></svg>
|
||||||
<thead>
|
</div>
|
||||||
<tr>
|
|
||||||
<th data-toggle="tooltip" data-placement="top" title="Time the setting got activated.">Activation Time</th>
|
|
||||||
<th data-toggle="tooltip" data-placement="top" title="Name of the global setting.">Setting</th>
|
|
||||||
<th data-toggle="tooltip" data-placement="top" title="Amount of time the setting is or was active">Active Time</th>
|
|
||||||
<th data-toggle="tooltip" data-placement="top" title="Market trends values from the time the setting got activated.">Market Trends</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@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 += "<br />";
|
|
||||||
}
|
|
||||||
marketTrendsSummary += Core.Helper.SystemHelper.SplitCamelCase(mt) + ": " + gss.MarketTrendChanges[mt].TrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
|
|
||||||
}
|
|
||||||
<tr>
|
|
||||||
<td>@settingActivationTime.DateTime.ToShortDateString() @settingActivationTime.DateTime.ToShortTimeString()</td>
|
|
||||||
<td>@Core.Helper.SystemHelper.SplitCamelCase(gss.SettingName)</td>
|
|
||||||
<td>@Core.Helper.SystemHelper.GetProperDurationTime(gss.ActiveSeconds)</td>
|
|
||||||
<td>@Html.Raw(marketTrendsSummary)</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</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>
|
||||||
|
<th data-toggle="tooltip" data-placement="top" title="Time the setting got activated.">Activation Time</th>
|
||||||
|
<th data-toggle="tooltip" data-placement="top" title="Name of the global setting.">Setting</th>
|
||||||
|
<th data-toggle="tooltip" data-placement="top" title="Amount of time the setting is or was active">Active Time</th>
|
||||||
|
<th data-toggle="tooltip" data-placement="top" title="Market trends values from the time the setting got activated.">Market Trends</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@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 += "<br />";
|
||||||
|
}
|
||||||
|
marketTrendsSummary += Core.Helper.SystemHelper.SplitCamelCase(mt) + ": " + gss.MarketTrendChanges[mt].TrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td>@settingActivationTime.DateTime.ToShortDateString() @settingActivationTime.DateTime.ToShortTimeString()</td>
|
||||||
|
<td>@Core.Helper.SystemHelper.SplitCamelCase(gss.SettingName)</td>
|
||||||
|
<td>@Core.Helper.SystemHelper.GetProperDurationTime(gss.ActiveSeconds)</td>
|
||||||
|
<td>@Html.Raw(marketTrendsSummary)</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-5 px-1">
|
<div class="col-md-5 px-1">
|
||||||
<div class="card-box px-2" style="height:305px;">
|
<div class="card-box px-2" style="height:320px;">
|
||||||
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
|
<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("")) {
|
@if (!Model.TrendChartDataJSON.Equals("")) {
|
||||||
<div class="trend-chart">
|
<div class="trend-chart">
|
||||||
<svg style="height: 300px;width: 100%;"></svg>
|
<svg style="height: 300px;width: 100%;"></svg>
|
||||||
|
@ -22,9 +22,10 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3 px-1">
|
<div class="col-md-3 px-1">
|
||||||
<div class="card-box px-3" style="height:305px;">
|
<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"></div>*@
|
<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"));
|
string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"));
|
||||||
if (Model.totalCurrentValue > 100) {
|
if (Model.totalCurrentValue > 100) {
|
||||||
|
@ -50,7 +51,8 @@
|
||||||
|
|
||||||
<div class="col-md-4 px-1">
|
<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="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("")) {
|
@if (!Model.ProfitChartDataJSON.Equals("")) {
|
||||||
<div class="profit-chart">
|
<div class="profit-chart">
|
||||||
<svg style="height:300px;width:100%"></svg>
|
<svg style="height:300px;width:100%"></svg>
|
||||||
|
@ -65,8 +67,8 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-5 px-1">
|
<div class="col-md-5 px-1">
|
||||||
<div class="card-box px-2">
|
<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>
|
@* <div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
|
||||||
<br>
|
<br> *@
|
||||||
<h4 class="m-t-0 m-b-20 header-title"><b>Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange</b>
|
<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>
|
<small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">more</a></small></h4>
|
||||||
<table class="table table-sm">
|
<table class="table table-sm">
|
||||||
|
@ -293,6 +295,22 @@
|
||||||
.datum(assetDistributionData)
|
.datum(assetDistributionData)
|
||||||
.transition().duration(0)
|
.transition().duration(0)
|
||||||
.call(assetDistributionChart);
|
.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;
|
return assetDistributionChart;
|
||||||
});
|
});
|
||||||
</text>
|
</text>
|
||||||
|
@ -325,6 +343,21 @@
|
||||||
.datum(trendData)
|
.datum(trendData)
|
||||||
.transition().duration(0)
|
.transition().duration(0)
|
||||||
.call(trendChart);
|
.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;
|
return trendChart;
|
||||||
});
|
});
|
||||||
</text>
|
</text>
|
||||||
|
@ -348,7 +381,7 @@
|
||||||
profitChart = nv.models.lineChart();
|
profitChart = nv.models.lineChart();
|
||||||
var height = 300;
|
var height = 300;
|
||||||
profitChart.useInteractiveGuideline(true);
|
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'));
|
profitChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f'));
|
||||||
|
|
||||||
profitData = @Html.Raw(Model.ProfitChartDataJSON);
|
profitData = @Html.Raw(Model.ProfitChartDataJSON);
|
||||||
|
@ -357,9 +390,54 @@
|
||||||
.datum(profitData)
|
.datum(profitData)
|
||||||
.transition().duration(0)
|
.transition().duration(0)
|
||||||
.call(profitChart);
|
.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;
|
return profitChart;
|
||||||
});
|
});
|
||||||
</text>
|
</text>
|
||||||
}
|
}
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
</script>
|
</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>
|
||||||
|
|
|
@ -8,6 +8,7 @@ using Core.Main.DataObjects;
|
||||||
using Core.Main.DataObjects.PTMagicData;
|
using Core.Main.DataObjects.PTMagicData;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Monitor.Pages
|
namespace Monitor.Pages
|
||||||
{
|
{
|
||||||
|
@ -19,6 +20,8 @@ namespace Monitor.Pages
|
||||||
public SummaryData SummaryData { get; set; }
|
public SummaryData SummaryData { get; set; }
|
||||||
|
|
||||||
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
|
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
|
||||||
|
public double DataHours { get; set; }
|
||||||
|
public int ProfitDays { get; set; }
|
||||||
public string TrendChartDataJSON = "";
|
public string TrendChartDataJSON = "";
|
||||||
public string ProfitChartDataJSON = "";
|
public string ProfitChartDataJSON = "";
|
||||||
public string LastGlobalSetting = "Default";
|
public string LastGlobalSetting = "Default";
|
||||||
|
@ -31,10 +34,9 @@ namespace Monitor.Pages
|
||||||
base.Init();
|
base.Init();
|
||||||
|
|
||||||
BindData();
|
BindData();
|
||||||
|
|
||||||
BuildAssetDistributionData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BindData()
|
private void BindData()
|
||||||
{
|
{
|
||||||
PTData = this.PtDataObject;
|
PTData = this.PtDataObject;
|
||||||
|
@ -62,110 +64,164 @@ namespace Monitor.Pages
|
||||||
MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList();
|
MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList();
|
||||||
|
|
||||||
BuildMarketTrendChartData();
|
BuildMarketTrendChartData();
|
||||||
|
BuildAssetDistributionData();
|
||||||
BuildProfitChartData();
|
BuildProfitChartData();
|
||||||
}
|
}
|
||||||
private void BuildMarketTrendChartData()
|
private void BuildMarketTrendChartData()
|
||||||
|
{
|
||||||
|
List<string> trendChartData = new List<string>();
|
||||||
|
if (MarketTrends.Count > 0)
|
||||||
{
|
{
|
||||||
List<string> trendChartData = new List<string>();
|
|
||||||
if (MarketTrends.Count > 0)
|
int mtIndex = 0;
|
||||||
|
foreach (MarketTrend mt in MarketTrends)
|
||||||
{
|
{
|
||||||
|
if (mt.DisplayGraph)
|
||||||
int mtIndex = 0;
|
|
||||||
foreach (MarketTrend mt in MarketTrends)
|
|
||||||
{
|
{
|
||||||
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
|
List<MarketTrendChange> marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name];
|
||||||
? Constants.ChartLineColors[mtIndex]
|
|
||||||
: Constants.ChartLineColors[mtIndex - 20];
|
|
||||||
|
|
||||||
if (Summary.MarketTrendChanges.ContainsKey(mt.Name))
|
if (marketTrendChangeSummaries.Count > 0)
|
||||||
{
|
{
|
||||||
List<MarketTrendChange> marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name];
|
List<string> trendValues = new List<string>();
|
||||||
|
|
||||||
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<string> trendValues = new List<string>();
|
offset = TimeSpan.Zero; // If offset is invalid, set it to zero
|
||||||
|
|
||||||
// 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<MarketTrendChange>.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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<MarketTrendChange>.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()
|
private void BuildProfitChartData()
|
||||||
|
{
|
||||||
|
List<object> profitPerDayList = new List<object>();
|
||||||
|
|
||||||
|
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;
|
offset = TimeSpan.Zero; // If offset is invalid, set it to zero
|
||||||
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() + "]}]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
// 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()
|
private void BuildAssetDistributionData()
|
||||||
{
|
{
|
||||||
|
@ -179,8 +235,7 @@ namespace Monitor.Pages
|
||||||
|
|
||||||
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);
|
string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive);
|
||||||
|
|
||||||
// Aggregate totals
|
// Aggregate totals
|
||||||
double leverage = dcaLogEntry.Leverage;
|
double leverage = dcaLogEntry.Leverage;
|
||||||
if (leverage == 0)
|
if (leverage == 0)
|
||||||
|
|
Loading…
Reference in New Issue