Sales Analyzer Page
This commit is contained in:
parent
b6b8647f21
commit
488e4c3fd3
|
@ -475,7 +475,16 @@ namespace Core.Main.DataObjects.PTMagicData
|
||||||
public int SoldTimes { get; set; }
|
public int SoldTimes { get; set; }
|
||||||
public double Avg { get; set; }
|
public double Avg { get; set; }
|
||||||
}
|
}
|
||||||
|
public class DailyStatsData
|
||||||
|
{
|
||||||
|
public string Date { get; set; }
|
||||||
|
public int TotalSales { get; set; }
|
||||||
|
public int TotalBuys { get; set; }
|
||||||
|
public double TotalProfit { get; set; }
|
||||||
|
public double AvgProfit { get; set; }
|
||||||
|
|
||||||
|
public double AvgGrowth { get; set; }
|
||||||
|
}
|
||||||
public class PTStrategy
|
public class PTStrategy
|
||||||
{
|
{
|
||||||
public string type { get; set; }
|
public string type { get; set; }
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace Core.Main.DataObjects
|
||||||
private List<DailyTCVData> _dailyTCV = new List<DailyTCVData>();
|
private List<DailyTCVData> _dailyTCV = new List<DailyTCVData>();
|
||||||
private List<MonthlyStatsData> _monthlyStats = new List<MonthlyStatsData>();
|
private List<MonthlyStatsData> _monthlyStats = new List<MonthlyStatsData>();
|
||||||
private List<ProfitablePairsData> _profitablePairs = new List<ProfitablePairsData>();
|
private List<ProfitablePairsData> _profitablePairs = new List<ProfitablePairsData>();
|
||||||
|
private List<DailyStatsData> _dailyStats = new List<DailyStatsData>();
|
||||||
private decimal? _totalProfit = null;
|
private decimal? _totalProfit = null;
|
||||||
public decimal? TotalProfit
|
public decimal? TotalProfit
|
||||||
{
|
{
|
||||||
|
@ -50,6 +51,7 @@ namespace Core.Main.DataObjects
|
||||||
private DateTime _miscRefresh = DateTime.UtcNow;
|
private DateTime _miscRefresh = DateTime.UtcNow;
|
||||||
private DateTime _propertiesRefresh = DateTime.UtcNow;
|
private DateTime _propertiesRefresh = DateTime.UtcNow;
|
||||||
private DateTime _profitablePairsRefresh = DateTime.UtcNow;
|
private DateTime _profitablePairsRefresh = DateTime.UtcNow;
|
||||||
|
private DateTime _dailyStatsRefresh = DateTime.UtcNow;
|
||||||
private volatile object _dailyPNLLock = new object();
|
private volatile object _dailyPNLLock = new object();
|
||||||
private volatile object _dailyTCVLock = new object();
|
private volatile object _dailyTCVLock = new object();
|
||||||
private volatile object _monthlyStatsLock = new object();
|
private volatile object _monthlyStatsLock = new object();
|
||||||
|
@ -60,6 +62,7 @@ namespace Core.Main.DataObjects
|
||||||
private volatile object _miscLock = new object();
|
private volatile object _miscLock = new object();
|
||||||
private volatile object _propertiesLock = new object();
|
private volatile object _propertiesLock = new object();
|
||||||
private volatile object _profitablePairsLock = new object();
|
private volatile object _profitablePairsLock = new object();
|
||||||
|
private volatile object _dailyStatsLock = new object();
|
||||||
private TimeSpan? _offsetTimeSpan = null;
|
private TimeSpan? _offsetTimeSpan = null;
|
||||||
public void DoLog(string message)
|
public void DoLog(string message)
|
||||||
{
|
{
|
||||||
|
@ -132,6 +135,74 @@ namespace Core.Main.DataObjects
|
||||||
TimeZoneOffset = PTData.timeZoneOffset,
|
TimeZoneOffset = PTData.timeZoneOffset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
public List<DailyStatsData> DailyStats
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh)
|
||||||
|
{
|
||||||
|
lock (_dailyStatsLock)
|
||||||
|
{
|
||||||
|
if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh)
|
||||||
|
{
|
||||||
|
using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats"))
|
||||||
|
using (var reader = new StreamReader(stream))
|
||||||
|
using (var jsonReader = new JsonTextReader(reader))
|
||||||
|
{
|
||||||
|
JObject basicSection = null;
|
||||||
|
JObject extraSection = null;
|
||||||
|
|
||||||
|
while (jsonReader.Read())
|
||||||
|
{
|
||||||
|
if (jsonReader.TokenType == JsonToken.PropertyName)
|
||||||
|
{
|
||||||
|
if ((string)jsonReader.Value == "basic")
|
||||||
|
{
|
||||||
|
jsonReader.Read(); // Move to the value of the "basic" property
|
||||||
|
basicSection = JObject.Load(jsonReader);
|
||||||
|
}
|
||||||
|
else if ((string)jsonReader.Value == "extra")
|
||||||
|
{
|
||||||
|
jsonReader.Read(); // Move to the value of the "extra" property
|
||||||
|
extraSection = JObject.Load(jsonReader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (basicSection != null && extraSection != null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (basicSection != null)
|
||||||
|
{
|
||||||
|
if (extraSection != null)
|
||||||
|
{
|
||||||
|
JArray dailyStatsSection = (JArray)extraSection["dailyStats"];
|
||||||
|
_dailyStats = dailyStatsSection.Select(j => BuildDailyStatsData(j as JObject)).ToList();
|
||||||
|
_dailyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _dailyStats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private DailyStatsData BuildDailyStatsData(dynamic dailyStatsDataJson)
|
||||||
|
{
|
||||||
|
return new DailyStatsData()
|
||||||
|
{
|
||||||
|
Date = dailyStatsDataJson["date"],
|
||||||
|
TotalSales = dailyStatsDataJson["totalSales"],
|
||||||
|
TotalBuys = dailyStatsDataJson["totalBuys"],
|
||||||
|
TotalProfit = dailyStatsDataJson["totalProfitCurrency"],
|
||||||
|
AvgProfit = dailyStatsDataJson["avgProfit"],
|
||||||
|
AvgGrowth = dailyStatsDataJson["avgGrowth"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public PropertiesData Properties
|
public PropertiesData Properties
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -198,7 +269,6 @@ namespace Core.Main.DataObjects
|
||||||
return _stats;
|
return _stats;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatsData BuildStatsData(dynamic statsDataJson)
|
private StatsData BuildStatsData(dynamic statsDataJson)
|
||||||
{
|
{
|
||||||
return new StatsData()
|
return new StatsData()
|
||||||
|
@ -295,10 +365,6 @@ namespace Core.Main.DataObjects
|
||||||
return _dailyPNL;
|
return _dailyPNL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public int GetTotalDays()
|
|
||||||
{
|
|
||||||
return DailyPNL?.Count ?? 0;
|
|
||||||
}
|
|
||||||
private DailyPNLData BuildDailyPNLData(dynamic dailyPNLDataJson)
|
private DailyPNLData BuildDailyPNLData(dynamic dailyPNLDataJson)
|
||||||
{
|
{
|
||||||
return new DailyPNLData()
|
return new DailyPNLData()
|
||||||
|
@ -345,14 +411,8 @@ namespace Core.Main.DataObjects
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (basicSection != null) // &&
|
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)
|
if (extraSection != null)
|
||||||
{
|
{
|
||||||
JObject profitablePairsSection = (JObject)extraSection["profitablePairs"];
|
JObject profitablePairsSection = (JObject)extraSection["profitablePairs"];
|
||||||
|
@ -390,10 +450,6 @@ namespace Core.Main.DataObjects
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public List<DailyTCVData> DailyTCV
|
public List<DailyTCVData> DailyTCV
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -433,15 +489,8 @@ namespace Core.Main.DataObjects
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (basicSection != null) // &&
|
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)
|
if (extraSection != null)
|
||||||
{
|
{
|
||||||
JArray dailyTCVSection = (JArray)extraSection["dailyTCVStats"];
|
JArray dailyTCVSection = (JArray)extraSection["dailyTCVStats"];
|
||||||
|
@ -509,14 +558,7 @@ namespace Core.Main.DataObjects
|
||||||
}
|
}
|
||||||
|
|
||||||
if (basicSection != null)// &&
|
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)
|
if (extraSection != null)
|
||||||
{
|
{
|
||||||
JArray monthlyStatsSection = (JArray)extraSection["monthlyStats"];
|
JArray monthlyStatsSection = (JArray)extraSection["monthlyStats"];
|
||||||
|
@ -532,11 +574,6 @@ namespace Core.Main.DataObjects
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetTotalMonths()
|
|
||||||
{
|
|
||||||
return MonthlyStats?.Count ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MonthlyStatsData BuildMonthlyStatsData(dynamic monthlyStatsDataJson)
|
private MonthlyStatsData BuildMonthlyStatsData(dynamic monthlyStatsDataJson)
|
||||||
{
|
{
|
||||||
return new MonthlyStatsData()
|
return new MonthlyStatsData()
|
||||||
|
@ -580,7 +617,7 @@ namespace Core.Main.DataObjects
|
||||||
this.BuildSellLogData(sellDataPage);
|
this.BuildSellLogData(sellDataPage);
|
||||||
pageIndex++;
|
pageIndex++;
|
||||||
requestedPages++;
|
requestedPages++;
|
||||||
Console.WriteLine($"Importing sale: {pageIndex}");
|
Console.WriteLine($"Importing salesLog: {pageIndex}");
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
@page
|
@page
|
||||||
@model SalesAnalyzer
|
@model SalesAnalyzer
|
||||||
|
@using System.Globalization
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "";
|
ViewData["Title"] = "";
|
||||||
}
|
}
|
||||||
|
@ -59,21 +60,39 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (Model.PTData.SellLog.Count == 0) {
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-6">
|
||||||
<div class="card-box">
|
<div class="card-box px-3" style="height:340px;">
|
||||||
<h4 class="m-t-0 header-title">No Sales!</h4>
|
@{
|
||||||
<p>Sorry, but your Profit Trailer did not sell anything so far. Please wait for the bot to have at least one sale and you will start seeing data in here.</p>
|
var days = Math.Min(Model.DailyStats.Count, 30);
|
||||||
|
}
|
||||||
|
<h4 class="m-t-0 m-b-20 header-title"><b>Daily Buys/Sales (@days Days) </b>
|
||||||
|
<div class="sales-chart">
|
||||||
|
<svg style="height:300px;width:100%"></svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
} else {
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card-box px-3" style="height:340px;">
|
||||||
|
<h4 class="m-t-0 m-b-20 header-title"><b>Daily Profit (All Time) </b>
|
||||||
|
@if (!Model.ProfitChartDataJSON.Equals("")) {
|
||||||
|
<div class="profit-chart">
|
||||||
|
<svg style="height:300px;width:100%"></svg>
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
<p>Unable to load graph, no sales data found.</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card-box">
|
<div class="card-box">
|
||||||
@{
|
@{
|
||||||
int totalDays = Model.PTData.GetTotalDays();
|
int totalDays = Model.PTData.DailyPNL.Count;
|
||||||
double startBalance = Model.MiscData.StartBalance;
|
double startBalance = Model.MiscData.StartBalance;
|
||||||
double totalSales = Model.PTData.Stats.TotalSales;
|
double totalSales = Model.PTData.Stats.TotalSales;
|
||||||
double totalProfit = Model.PTData.Stats.TotalProfit;
|
double totalProfit = Model.PTData.Stats.TotalProfit;
|
||||||
|
@ -92,14 +111,14 @@
|
||||||
double avgMonthlyGain = totalPercentGain / totalMonths;
|
double avgMonthlyGain = totalPercentGain / totalMonths;
|
||||||
double avgMonthlyFunding = totalFundingFees / totalMonths;
|
double avgMonthlyFunding = totalFundingFees / totalMonths;
|
||||||
}
|
}
|
||||||
<h4 class="m-t-0 header-title" style="display: inline;">Sales Analysis</h4> <span style="font-size: x-small"> (@startDate - @endDate)</span>
|
<h4 class="m-t-0 header-title" style="display: inline;">Averages</h4> <span style="font-size: x-small"> (@startDate - @endDate)</span>
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th class="text-right">Total</th>
|
<th class="text-right">Total</th>
|
||||||
<th class="text-right">AVG/Day (@totalDays Days)</th>
|
<th class="text-right">AVG/Day</span></th>
|
||||||
<th class="text-right">AVG/Month (@totalMonths Months) <small><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top"
|
<th class="text-right">AVG/Month (@totalMonths Months)</span> <small><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top"
|
||||||
title="Total months is based on the size of the actual calendar months."></i></small></th>
|
title="Total months is based on the size of the actual calendar months."></i></small></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -111,7 +130,7 @@
|
||||||
<td class="text-right">@avgMonthlySales</td>
|
<td class="text-right">@avgMonthlySales</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Profit @Model.PTData.Misc.Market</th>
|
<th> @Model.PTData.Misc.Market</th>
|
||||||
<td class="text-right text-autocolor">@totalProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
<td class="text-right text-autocolor">@totalProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
<td class="text-right text-autocolor">@Math.Round(totalProfit / totalDays, 8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
<td class="text-right text-autocolor">@Math.Round(totalProfit / totalDays, 8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
<td class="text-right text-autocolor">@avgMonthlyProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
<td class="text-right text-autocolor">@avgMonthlyProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
|
@ -119,14 +138,14 @@
|
||||||
@if(Model.PropertiesData.IsLeverageExchange)
|
@if(Model.PropertiesData.IsLeverageExchange)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<th>Funding Fees</th>
|
<th>Funding</th>
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalFundingFees,8).ToString("#0.00000000", new System.Globalization.CultureInfo("en-US")))</td>
|
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalFundingFees,8).ToString("#0.00000000", new System.Globalization.CultureInfo("en-US")))</td>
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalFundingFees / totalDays,8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")))</td>
|
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalFundingFees / totalDays,8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")))</td>
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalFundingFees / totalMonths,8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")))</td>
|
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalFundingFees / totalMonths,8).ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")))</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
<tr>
|
<tr>
|
||||||
<th>Profit @Model.PTData.Properties.Currency</th>
|
<th>@Model.PTData.Properties.Currency</th>
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalProfitFiat,0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
|
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalProfitFiat,0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalProfitFiat / totalDays, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
|
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalProfitFiat / totalDays, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalProfitFiat / totalMonths, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
|
<td class="text-right text-autocolor">@Html.Raw(Math.Round(totalProfitFiat / totalMonths, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
|
||||||
|
@ -200,30 +219,87 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card-box px-3" style="height:340px;">
|
<div class="card-box">
|
||||||
<h4 class="m-t-0 m-b-20 header-title"><b>Daily Sales </b>
|
@{
|
||||||
<div class="trades-chart">
|
var maxDays = Math.Min(Model.DailyStats.Count, Math.Min(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries, 30));
|
||||||
<svg style="height:300px;width:100%"></svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card-box px-3" style="height:340px;">
|
|
||||||
<h4 class="m-t-0 m-b-20 header-title"><b>Daily Profit </b>
|
|
||||||
@if (!Model.ProfitChartDataJSON.Equals("")) {
|
|
||||||
<div class="profit-chart">
|
|
||||||
<svg style="height:300px;width:100%"></svg>
|
|
||||||
</div>
|
|
||||||
} else {
|
|
||||||
<p>Unable to load graph, no sales data found.</p>
|
|
||||||
}
|
}
|
||||||
</div>
|
<h4 class="m-t-0 header-title">Last @maxDays Days</h4>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Day</th>
|
||||||
|
<th class="text-right">Buys</th>
|
||||||
|
<th class="text-right">Sales</th>
|
||||||
|
<th class="text-right">Profit @Model.Summary.MainMarket</th>
|
||||||
|
<th class="text-right">Profit @Model.PTData.Properties.Currency</th>
|
||||||
|
<th class="text-right">% Gain</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@{
|
||||||
|
for (int i = 0; i < maxDays; i++)
|
||||||
|
{
|
||||||
|
DateTime salesDate = DateTime.ParseExact(Model.PTData.DailyStats[i].Date, "d-M-yyyy", CultureInfo.InvariantCulture);
|
||||||
|
var buys = Model.PTData.DailyStats[i].TotalBuys;
|
||||||
|
var sales = Model.PTData.DailyStats[i].TotalSales;
|
||||||
|
var profit = Model.PTData.DailyStats[i].TotalProfit;
|
||||||
|
var avgProfit = Model.PTData.DailyStats[i].AvgProfit;
|
||||||
|
var profitFiat = Math.Round(profit * Model.PTData.Misc.FiatConversionRate, 0);
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@salesDate.ToString("yyyy-MM-dd")</td>
|
||||||
|
<td class="text-right text-autocolor"">@buys</td>
|
||||||
|
<td class="text-right text-autocolor"">@sales</td>
|
||||||
|
<td class="text-right text-autocolor">@profit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
|
<td class="text-right text-autocolor">@Math.Round(profitFiat,0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
|
<td class="text-right text-autocolor">@avgProfit.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="col-sm-6">
|
||||||
<div class="col-sm-12">
|
<div class="card-box">
|
||||||
|
@{
|
||||||
|
var maxMonths = Math.Min(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries, Model.MonthlyStats.Count);
|
||||||
|
}
|
||||||
|
<h4 class="m-t-0 header-title">Last @maxMonths months</h4>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Month</th>
|
||||||
|
<th class="text-right">Sales</th>
|
||||||
|
<th class="text-right">Profit @Model.Summary.MainMarket</th>
|
||||||
|
<th class="text-right">Profit @Model.PTData.Properties.Currency</th>
|
||||||
|
<th class="text-right">% Growth</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@{
|
||||||
|
for (int i = 0; i < maxMonths; i++)
|
||||||
|
{
|
||||||
|
DateTime monthDate = DateTime.ParseExact(Model.PTData.MonthlyStats[i].Month, "M-yyyy", CultureInfo.InvariantCulture);
|
||||||
|
string monthName = monthDate.ToString("MMMM yyyy", CultureInfo.InvariantCulture);
|
||||||
|
var sales = Model.PTData.MonthlyStats[i].TotalSales;
|
||||||
|
var profit = Model.PTData.MonthlyStats[i].TotalProfitCurrency;
|
||||||
|
var profitFiat = Math.Round(profit * Model.PTData.Misc.FiatConversionRate, 0);
|
||||||
|
var growth = Model.PTData.MonthlyStats[i].AvgGrowth;
|
||||||
|
<tr>
|
||||||
|
<td>@monthName</td>
|
||||||
|
<td class="text-right text-autocolor">@sales</td>
|
||||||
|
<td class="text-right text-autocolor">@profit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
|
<td class="text-right text-autocolor">@profitFiat</td>
|
||||||
|
<td class="text-right text-autocolor">@growth %</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card-box">
|
<div class="card-box">
|
||||||
<h4 class="m-t-0 header-title"><b>Top @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets Sales Markets</b></h4>
|
<h4 class="m-t-0 header-title"><b>Top @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets Sales Markets</b></h4>
|
||||||
<table class="tablesaw table m-b-0" data-tablesaw-sortable data-tablesaw-sortable-switch>
|
<table class="tablesaw table m-b-0" data-tablesaw-sortable data-tablesaw-sortable-switch>
|
||||||
|
@ -262,109 +338,8 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card-box">
|
|
||||||
<h4 class="m-t-0 header-title">Last @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries days</h4>
|
|
||||||
<table class="table table-sm">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Day</th>
|
|
||||||
<th class="text-right">Sales</th>
|
|
||||||
<th class="text-right">Profit @Model.Summary.MainMarket</th>
|
|
||||||
<th class="text-right">Profit @Model.Summary.MainFiatCurrency</th>
|
|
||||||
<th class="text-right">% Gain</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@for (DateTime salesDate = Model.DateTimeNow.DateTime.Date; salesDate >= Model.DateTimeNow.DateTime.AddDays(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries) && salesDate >= Model.MinSellLogDate; salesDate = salesDate.AddDays(-1)) {
|
|
||||||
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesDateSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate);
|
|
||||||
double salesDateProfit = 0;
|
|
||||||
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
|
|
||||||
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2);
|
|
||||||
double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesDate);
|
|
||||||
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
|
|
||||||
<tr>
|
|
||||||
<td><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)_get/SalesList/?d=@salesDate.ToString("yyyy-MM-dd")" data-remote="false" data-toggle="modal" data-target="#salesList">@salesDate.ToShortDateString()</a></td>
|
|
||||||
<td class="text-right">@salesDateSales.Count</td>
|
|
||||||
<td class="text-right text-autocolor">@salesDateProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + salesDateProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td>
|
|
||||||
<td class="text-right text-autocolor">@salesDateGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card-box">
|
|
||||||
<h4 class="m-t-0 header-title">Last @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries months</h4>
|
|
||||||
|
|
||||||
<table class="table table-sm">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Month</th>
|
|
||||||
<th class="text-right">Sales</th>
|
|
||||||
<th class="text-right">Profit @Model.Summary.MainMarket</th>
|
|
||||||
<th class="text-right">Profit @Model.Summary.MainFiatCurrency</th>
|
|
||||||
<th class="text-right">% Gain</th>
|
|
||||||
<th class="text-right">AVG %/Day</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@{
|
|
||||||
DateTime minSellLogMonthDate = new DateTime(Model.MinSellLogDate.Year, Model.MinSellLogDate.Month, 1).Date;
|
|
||||||
DateTime salesMonthStartDate = new DateTime(Model.DateTimeNow.DateTime.Year, Model.DateTimeNow.DateTime.Month, 1).Date;
|
|
||||||
}
|
|
||||||
@for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= Model.DateTimeNow.DateTime.AddMonths(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries) && salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) {
|
|
||||||
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesMonthSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year);
|
|
||||||
double salesDateProfit = 0;
|
|
||||||
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
|
|
||||||
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2);
|
|
||||||
double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesMonthDate);
|
|
||||||
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
|
|
||||||
double salesDateAVGDailyGain = 0;
|
|
||||||
double monthDailyProfit = 0;
|
|
||||||
int days = 0;
|
|
||||||
for (int d = 1; d <= DateTime.DaysInMonth(salesMonthDate.Year, salesMonthDate.Month); d++) {
|
|
||||||
DateTime monthDay = salesMonthDate.AddDays(-salesMonthDate.Day + d);
|
|
||||||
if (monthDay <= Model.DateTimeNow) {
|
|
||||||
days++;
|
|
||||||
List<Core.Main.DataObjects.PTMagicData.SellLogData> monthDaySales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == monthDay.Date);
|
|
||||||
double monthDayProfit = 0;
|
|
||||||
monthDayProfit = monthDaySales.Sum(sl => sl.Profit);
|
|
||||||
double monthDayStartBalance = Model.PTData.GetSnapshotBalance(monthDay);
|
|
||||||
monthDailyProfit += Math.Round(monthDayProfit / monthDayStartBalance * 100, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
salesDateAVGDailyGain = Math.Round(monthDailyProfit / days, 2);
|
|
||||||
<tr>
|
|
||||||
<td><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)_get/SalesList/?m=@salesMonthDate.ToString("yyyy-MM")" data-remote="false" data-toggle="modal" data-target="#salesList">@salesMonthDate.ToString("MMMM", new System.Globalization.CultureInfo("en-US"))</a></td>
|
|
||||||
<td class="text-right text-autocolor">@salesMonthSales.Count</td>
|
|
||||||
<td class="text-right text-autocolor">@salesDateProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
|
||||||
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + salesDateProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td>
|
|
||||||
<td class="text-right text-autocolor">@salesDateGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
|
||||||
<td class="text-right text-autocolor">@salesDateAVGDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@* <div id="salesList" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" style="display: none;">
|
|
||||||
<div class="modal-dialog modal-full">
|
|
||||||
<div class="modal-content">
|
|
||||||
<i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i>
|
|
||||||
</div><!-- /.modal-content -->
|
|
||||||
</div><!-- /.modal-dialog -->
|
|
||||||
</div><!-- /.modal --> *@
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
|
@ -387,32 +362,54 @@
|
||||||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/datatables/buttons.print.min.js"></script>
|
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/datatables/buttons.print.min.js"></script>
|
||||||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/datatables/buttons.colVis.min.js"></script>
|
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/datatables/buttons.colVis.min.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function ($) {
|
(function ($) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
$('[role="tooltip"]').remove();
|
||||||
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
$('.text-autocolor').autocolor(false);
|
||||||
|
|
||||||
|
var salesChart; // Keep a reference to the chart
|
||||||
|
var salesData; // Keep a reference to the data
|
||||||
|
|
||||||
|
@if (!Model.SalesChartDataJSON.Equals("")) {
|
||||||
|
<text>
|
||||||
nv.addGraph(function () {
|
nv.addGraph(function () {
|
||||||
var lineChart = nv.models.lineChart();
|
salesChart = nv.models.lineChart();
|
||||||
var height = 300;
|
var height = 300;
|
||||||
/**/
|
salesChart.useInteractiveGuideline(true);
|
||||||
var chartData = @Html.Raw(Model.TradesChartDataJSON);
|
salesChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); });
|
||||||
/**/
|
salesChart.yAxis.axisLabel('').tickFormat(d3.format(',.2f'));
|
||||||
lineChart.useInteractiveGuideline(true);
|
|
||||||
lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); });
|
salesData = @Html.Raw(Model.SalesChartDataJSON);
|
||||||
lineChart.yAxis.axisLabel('Daily Sales').tickFormat(d3.format(','));
|
|
||||||
d3.select('.trades-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
|
d3.select('.sales-chart svg')
|
||||||
nv.utils.windowResize(lineChart.update);
|
.datum(salesData)
|
||||||
return lineChart;
|
.transition().duration(0)
|
||||||
|
.call(salesChart);
|
||||||
|
|
||||||
|
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||||
|
d3.select('.sales-chart').on('mouseleave', function() {
|
||||||
|
d3.select('.nvtooltip').style('opacity', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#salesList").on("show.bs.modal", function (e) {
|
d3.select('body').on('mousemove', function() {
|
||||||
$(this).find(".modal-content").html('<i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i>');
|
var chartBounds = d3.select('.sales-chart')[0][0].getBoundingClientRect();
|
||||||
var link = $(e.relatedTarget);
|
var mouseX = d3.event.clientX;
|
||||||
$(this).find(".modal-content").load(link.attr("href"), function () {
|
var mouseY = d3.event.clientY;
|
||||||
$('.text-autocolor').autocolor(false);
|
|
||||||
|
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||||
|
d3.select('.nvtooltip').style('opacity', 0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
nv.utils.windowResize(salesChart.update);
|
||||||
|
return salesChart;
|
||||||
});
|
});
|
||||||
|
</text>
|
||||||
|
}
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function ($) {
|
(function ($) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ using System.Globalization;
|
||||||
using Core.Main.DataObjects;
|
using Core.Main.DataObjects;
|
||||||
using Core.Main.DataObjects.PTMagicData;
|
using Core.Main.DataObjects.PTMagicData;
|
||||||
|
|
||||||
|
|
||||||
namespace Monitor.Pages
|
namespace Monitor.Pages
|
||||||
{
|
{
|
||||||
public class SalesAnalyzer : _Internal.BasePageModelSecure
|
public class SalesAnalyzer : _Internal.BasePageModelSecure
|
||||||
|
@ -17,21 +18,21 @@ namespace Monitor.Pages
|
||||||
public List<DailyPNLData> DailyPNL { get; set; }
|
public List<DailyPNLData> DailyPNL { get; set; }
|
||||||
public List<DailyTCVData> DailyTCV { get; set; }
|
public List<DailyTCVData> DailyTCV { get; set; }
|
||||||
public List<ProfitablePairsData> ProfitablePairs { get; set; }
|
public List<ProfitablePairsData> ProfitablePairs { get; set; }
|
||||||
|
public List<DailyStatsData> DailyStats { get; set; }
|
||||||
public int ProfitDays { get; set; }
|
public int ProfitDays { get; set; }
|
||||||
public int TCVDays { get; set; }
|
public int TCVDays { get; set; }
|
||||||
|
public int SalesDays { get; set; }
|
||||||
public List<MonthlyStatsData> MonthlyStats { get; set; }
|
public List<MonthlyStatsData> MonthlyStats { get; set; }
|
||||||
|
|
||||||
public string TradesChartDataJSON = "";
|
public string TradesChartDataJSON = "";
|
||||||
public string CumulativeProfitChartDataJSON = "";
|
public string CumulativeProfitChartDataJSON = "";
|
||||||
public string TCVChartDataJSON = "";
|
public string TCVChartDataJSON = "";
|
||||||
public string ProfitChartDataJSON = "";
|
public string ProfitChartDataJSON = "";
|
||||||
public string BalanceChartDataJSON = "";
|
public string SalesChartDataJSON = "";
|
||||||
public IEnumerable<KeyValuePair<string, double>> TopMarkets = null;
|
public IEnumerable<KeyValuePair<string, double>> TopMarkets = null;
|
||||||
public DateTime MinSellLogDate = Constants.confMinDate;
|
public DateTime MinSellLogDate = Constants.confMinDate;
|
||||||
public Dictionary<DateTime, double> DailyGains = new Dictionary<DateTime, double>();
|
|
||||||
public Dictionary<DateTime, double> MonthlyGains = new Dictionary<DateTime, double>();
|
|
||||||
public DateTimeOffset DateTimeNow = Constants.confMinDate;
|
public DateTimeOffset DateTimeNow = Constants.confMinDate;
|
||||||
public double totalCurrentValue = 0;
|
|
||||||
|
|
||||||
public void OnGet()
|
public void OnGet()
|
||||||
{
|
{
|
||||||
|
@ -50,6 +51,7 @@ namespace Monitor.Pages
|
||||||
DailyPNL = this.PTData.DailyPNL;
|
DailyPNL = this.PTData.DailyPNL;
|
||||||
DailyTCV = this.PTData.DailyTCV;
|
DailyTCV = this.PTData.DailyTCV;
|
||||||
ProfitablePairs = this.PTData.ProfitablePairs;
|
ProfitablePairs = this.PTData.ProfitablePairs;
|
||||||
|
DailyStats = this.PTData.DailyStats;
|
||||||
|
|
||||||
// Convert local offset time to UTC
|
// Convert local offset time to UTC
|
||||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||||
|
@ -60,6 +62,7 @@ namespace Monitor.Pages
|
||||||
BuildCumulativeProfitChartData();
|
BuildCumulativeProfitChartData();
|
||||||
BuildTCVChartData();
|
BuildTCVChartData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildTCVChartData()
|
private void BuildTCVChartData()
|
||||||
{
|
{
|
||||||
List<object> TCVPerDayList = new List<object>();
|
List<object> TCVPerDayList = new List<object>();
|
||||||
|
@ -284,51 +287,68 @@ namespace Monitor.Pages
|
||||||
weight = 1;
|
weight = 1;
|
||||||
}
|
}
|
||||||
totalMonths += weight;
|
totalMonths += weight;
|
||||||
//Console.WriteLine("Month: {0}, Weight: {1}", monthStat.Month, weight);
|
|
||||||
}
|
}
|
||||||
return (totalMonths, startDate, endDate);
|
return (totalMonths, startDate, endDate);
|
||||||
}
|
}
|
||||||
private void BuildTopMarkets()
|
|
||||||
{
|
|
||||||
var markets = PTData.SellLog.GroupBy(m => m.Market);
|
|
||||||
Dictionary<string, double> topMarketsDic = new Dictionary<string, double>();
|
|
||||||
foreach (var market in markets)
|
|
||||||
{
|
|
||||||
double totalProfit = 0;
|
|
||||||
totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit);
|
|
||||||
topMarketsDic.Add(market.Key, totalProfit);
|
|
||||||
}
|
|
||||||
TopMarkets = new SortedDictionary<string, double>(topMarketsDic).OrderByDescending(m => m.Value).Take(PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets);
|
|
||||||
}
|
|
||||||
private void BuildSalesChartData()
|
private void BuildSalesChartData()
|
||||||
{
|
{
|
||||||
if (PTData.SellLog.Count > 0)
|
List<object> salesPerDayList = new List<object>();
|
||||||
{
|
List<object> buysPerDayList = new List<object>(); // New list for daily buys
|
||||||
MinSellLogDate = PTData.SellLog.OrderBy(sl => sl.SoldDate).First().SoldDate.Date;
|
|
||||||
DateTime graphStartDate = DateTimeNow.DateTime.Date.AddDays(-1850);
|
|
||||||
if (MinSellLogDate > graphStartDate) graphStartDate = MinSellLogDate;
|
|
||||||
|
|
||||||
int tradeDayIndex = 0;
|
if (PTData.DailyStats.Count > 0)
|
||||||
string tradesPerDayJSON = "";
|
|
||||||
for (DateTime salesDate = graphStartDate; salesDate <= DateTimeNow.DateTime.Date; salesDate = salesDate.AddDays(1))
|
|
||||||
{
|
{
|
||||||
if (tradeDayIndex > 0)
|
// Get timezone offset
|
||||||
|
TimeSpan offset;
|
||||||
|
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||||
|
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||||
|
|
||||||
|
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||||
{
|
{
|
||||||
tradesPerDayJSON += ",\n";
|
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, DailyStatsData> salesCountByDate = PTData.DailyStats
|
||||||
|
.ToDictionary(
|
||||||
|
data => DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture),
|
||||||
|
data => data
|
||||||
|
);
|
||||||
|
|
||||||
|
DateTime earliestDataDate = salesCountByDate.Keys.Min();
|
||||||
|
DateTime startDate = earliestDataDate;
|
||||||
|
|
||||||
|
// Calculate the total days of data available
|
||||||
|
SalesDays = (endDate - startDate).Days;
|
||||||
|
int counter = 0;
|
||||||
|
for (DateTime date = startDate; date <= endDate && counter < 30; date = date.AddDays(1)) // Check the counter
|
||||||
|
{
|
||||||
|
if (salesCountByDate.TryGetValue(date, out DailyStatsData dailyStatsData))
|
||||||
|
{
|
||||||
|
// Use the totalSales value directly
|
||||||
|
salesPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = dailyStatsData.TotalSales });
|
||||||
|
|
||||||
|
// Add daily buys to the list
|
||||||
|
buysPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = dailyStatsData.TotalBuys });
|
||||||
}
|
}
|
||||||
int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Count;
|
|
||||||
tradesPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + trades + "}";
|
|
||||||
tradeDayIndex++;
|
|
||||||
}
|
}
|
||||||
TradesChartDataJSON = "[";
|
|
||||||
TradesChartDataJSON += "{";
|
|
||||||
TradesChartDataJSON += "key: 'Sales',";
|
|
||||||
TradesChartDataJSON += "color: '" + Constants.ChartLineColors[0] + "',";
|
|
||||||
TradesChartDataJSON += "values: [" + tradesPerDayJSON + "]";
|
|
||||||
TradesChartDataJSON += "}";
|
|
||||||
TradesChartDataJSON += "]";
|
|
||||||
|
|
||||||
|
|
||||||
|
// Convert the lists to a JSON string using Newtonsoft.Json
|
||||||
|
SalesChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] {
|
||||||
|
new {
|
||||||
|
key = "Sales",
|
||||||
|
color = Constants.ChartLineColors[1],
|
||||||
|
values = salesPerDayList
|
||||||
|
},
|
||||||
|
new { // New JSON object for daily buys
|
||||||
|
key = "Buys",
|
||||||
|
color = Constants.ChartLineColors[0], // Use a different color for buys
|
||||||
|
values = buysPerDayList
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,7 +217,7 @@
|
||||||
</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">Dashboard Bottom Refresh <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval in seconds, of the charts and graphs on then main page."></i></label>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<input type="text" class="form-control" name="Monitor_RefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
|
<input type="text" class="form-control" name="Monitor_RefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
|
|
||||||
<div class="col-md-3 px-1">
|
<div class="col-md-3 px-1">
|
||||||
<div class="card-box px-3" style="height:340px;">
|
<div class="card-box px-3" style="height:340px;">
|
||||||
<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"
|
||||||
|
title="All charts set to refresh every @Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds seconds in your general settings."></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) {
|
||||||
|
@ -66,18 +67,21 @@
|
||||||
|
|
||||||
<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-3">
|
||||||
@* <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.RefreshSeco;nds * 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">Live Trends <small> (@Model.PTMagicConfiguration.GeneralSettings.Application.Exchange)  </small>
|
||||||
<small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">more</a></small></h4>
|
<i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Set to refresh every @Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds seconds in general settings."></i>
|
||||||
|
<small class="pull-right" style="font-size: small"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">ANALYZER</a></small>
|
||||||
|
</h4>
|
||||||
<table class="table table-sm">
|
<table class="table table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th class="text-right">Markets</th>
|
<th class="text-right">Markets</th>
|
||||||
<th class="text-right">Timeframe</th>
|
<th class="text-right">Timeframe</th>
|
||||||
<th class="text-right" data-toggle="tooltip" data-placement="top" title="Pairs exceeding this threshold are excluded from the trend average.">Threshold %</th>
|
<th class="text-right">Threshold <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Pairs exceeding this threshold are excluded from the trend average."></i>
|
||||||
|
</th>
|
||||||
<th class="text-right">Change</th>
|
<th class="text-right">Change</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -109,9 +113,9 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<td class="text-right">@marketTrend.TrendThreshold</td>
|
<td class="text-right">@marketTrend.TrendThreshold %</td>
|
||||||
}
|
}
|
||||||
<td class="text-right text-autocolor">@trendChangeOutput%</td>
|
<td class="text-right text-autocolor" style="font-weight:bold;">@trendChangeOutput%</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,10 +125,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-7 px-1">
|
<div class="col-md-7 px-1">
|
||||||
<div class="card-box px-2">
|
<div class="card-box px-3">
|
||||||
@*<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">Sales Overview
|
||||||
<br>*@
|
<i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="All data acquired via Profit Trailer API."></i>
|
||||||
<h4 class="m-t-0 m-b-20 header-title"><b>Sales Overview</b><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SalesAnalyzer">more</a></small></h4>
|
<small class="pull-right" style="font-size: small"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SalesAnalyzer">ANALYZER</a></small>
|
||||||
|
</h4>
|
||||||
@{
|
@{
|
||||||
|
|
||||||
double avgGrowthThisMonth = Model.PTData.MonthlyStats.FirstOrDefault(data => data.Order == 1)?.AvgGrowth ?? 0.0;
|
double avgGrowthThisMonth = Model.PTData.MonthlyStats.FirstOrDefault(data => data.Order == 1)?.AvgGrowth ?? 0.0;
|
||||||
|
@ -182,7 +187,7 @@
|
||||||
<th class="text-right">Profit @Model.PTData.Misc.Market</th>
|
<th class="text-right">Profit @Model.PTData.Misc.Market</th>
|
||||||
@if (futuresFunding)
|
@if (futuresFunding)
|
||||||
{
|
{
|
||||||
<th class="text-right">Funding @Model.PTData.Misc.Market</th>
|
<th class="text-right">Funding</th>
|
||||||
}
|
}
|
||||||
<th class="text-right">@Model.PTData.Properties.Currency</th>
|
<th class="text-right">@Model.PTData.Properties.Currency</th>
|
||||||
<th class="text-right">Gain</th>
|
<th class="text-right">Gain</th>
|
||||||
|
@ -212,7 +217,7 @@
|
||||||
<td class="text-right text-autocolor">@yesterdaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
<td class="text-right text-autocolor">@yesterdaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Last 7 Days</th>
|
<th>7 Days</th>
|
||||||
<td class="text-right">@last7DaysSales</td>
|
<td class="text-right">@last7DaysSales</td>
|
||||||
<td class="text-right text-autocolor">@last7DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
<td class="text-right text-autocolor">@last7DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
@if (futuresFunding)
|
@if (futuresFunding)
|
||||||
|
@ -223,7 +228,15 @@
|
||||||
<td class="text-right text-autocolor">@last7DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
<td class="text-right text-autocolor">@last7DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>This Month</th>
|
@{
|
||||||
|
var timeParts = @Model.MiscData.TimeZoneOffset.Split(':');
|
||||||
|
var timeZoneOffsetHours = int.Parse(timeParts[0]);
|
||||||
|
var timeZoneOffset = TimeSpan.FromHours(timeZoneOffsetHours);
|
||||||
|
var timeZoneInfo = TimeZoneInfo.CreateCustomTimeZone("Custom", timeZoneOffset, "Custom", "Custom");
|
||||||
|
var currentDateTime = TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZoneInfo);
|
||||||
|
var currentMonthName = currentDateTime.ToString("MMMM");
|
||||||
|
}
|
||||||
|
<th>@currentMonthName</th>
|
||||||
<td class="text-right">@thisMonthSales</td>
|
<td class="text-right">@thisMonthSales</td>
|
||||||
<td class="text-right text-autocolor">@thisMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
<td class="text-right text-autocolor">@thisMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
@if (futuresFunding)
|
@if (futuresFunding)
|
||||||
|
@ -234,7 +247,11 @@
|
||||||
<td class="text-right text-autocolor">@thisMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
<td class="text-right text-autocolor">@thisMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Last Month</th>
|
@{
|
||||||
|
var previousMonthDateTime = currentDateTime.AddMonths(-1);
|
||||||
|
var previousMonthName = previousMonthDateTime.ToString("MMMM");
|
||||||
|
}
|
||||||
|
<th>@previousMonthName</th>
|
||||||
<td class="text-right">@lastMonthSales</td>
|
<td class="text-right">@lastMonthSales</td>
|
||||||
<td class="text-right text-autocolor">@lastMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
<td class="text-right text-autocolor">@lastMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||||
@if (futuresFunding)
|
@if (futuresFunding)
|
||||||
|
|
Loading…
Reference in New Issue