More Dashboard Bottom Optimizations

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

View File

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

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Diagnostics;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Core.Main.DataObjects.PTMagicData;
@ -17,15 +18,25 @@ namespace Core.Main.DataObjects
private SummaryData _summary = null;
private PropertiesData _properties = null;
private StatsData _stats = null;
private List<DailyStatsData> _dailyStats = new List<DailyStatsData>();
private List<DailyPNLData> _dailyPNL = new List<DailyPNLData>();
private decimal? _totalProfit = null;
public decimal? TotalProfit
{
get { return _totalProfit; }
set { _totalProfit = value; }
}
private decimal? _totalSales = null;
public decimal? TotalSales
{
get { return _totalSales; }
set { _totalSales = value; }
}
private List<SellLogData> _sellLog = new List<SellLogData>();
private List<DCALogData> _dcaLog = new List<DCALogData>();
private List<BuyLogData> _buyLog = new List<BuyLogData>();
private string _ptmBasePath = "";
private PTMagicConfiguration _systemConfiguration = null;
private TransactionData _transactionData = null;
private DateTime _dailyStatsRefresh = DateTime.UtcNow;
private DateTime _dailyPNLRefresh = DateTime.UtcNow;
private DateTime _statsRefresh = DateTime.UtcNow;
private DateTime _buyLogRefresh = DateTime.UtcNow;
@ -143,6 +154,28 @@ namespace Core.Main.DataObjects
BaseUrl = PTProperties.baseUrl
};
}
// public StatsData Stats
// {
// get
// {
// if (_stats == null || DateTime.UtcNow > _statsRefresh)
// {
// lock (_statsLock)
// {
// if (_stats == null || DateTime.UtcNow > _statsRefresh)
// {
// dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats");
// JObject statsDataJObject = statsDataJson as JObject;
// JObject basicSection = (JObject)statsDataJObject["basic"];
// _stats = BuildStatsData(basicSection);
// _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
// }
// }
// }
// return _stats;
// }
// }
public StatsData Stats
{
get
@ -153,17 +186,29 @@ namespace Core.Main.DataObjects
{
if (_stats == null || DateTime.UtcNow > _statsRefresh)
{
dynamic statsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats");
JObject statsDataJObject = statsDataJson as JObject;
JObject basicSection = (JObject)statsDataJObject["basic"];
_stats = BuildStatsData(basicSection);
_statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats"))
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader))
{
while (jsonReader.Read())
{
if (jsonReader.TokenType == JsonToken.PropertyName && (string)jsonReader.Value == "basic")
{
jsonReader.Read(); // Move to the value of the "basic" property
JObject basicSection = JObject.Load(jsonReader);
_stats = BuildStatsData(basicSection);
_statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
break;
}
}
}
}
}
}
return _stats;
}
}
private StatsData BuildStatsData(dynamic statsDataJson)
{
return new StatsData()
@ -194,38 +239,28 @@ namespace Core.Main.DataObjects
FundingTotal = statsDataJson["totalFunding"]
};
}
public List<DailyStatsData> DailyStats
{
get
{
if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh)
{
lock (_dailyStatsLock)
{
if (_dailyStats == null || DateTime.UtcNow > _dailyStatsRefresh)
{
dynamic dailyStatsDataJson = GetDataFromProfitTrailer("/api/v2/data/stats");
JObject dailyStatsDataJObject = dailyStatsDataJson as JObject;
JArray dailyStatsSection = (JArray)dailyStatsDataJObject["extra"]["dailyStats"];
_dailyStats = dailyStatsSection.Select(j => BuildDailyStatsData(j as JObject)).ToList();
_dailyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
}
}
}
return _dailyStats;
}
}
private DailyStatsData BuildDailyStatsData(dynamic dailyStatsDataJson)
{
return new DailyStatsData()
{
Date = dailyStatsDataJson["date"],
TotalSales = dailyStatsDataJson["totalSales"],
TotalBuys = dailyStatsDataJson["totalBuys"],
TotalProfitCurrency = dailyStatsDataJson["totalProfitCurrency"],
Order = dailyStatsDataJson["order"],
};
}
// public List<DailyPNLData> DailyPNL
// {
// get
// {
// if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
// {
// lock (_dailyPNLLock)
// {
// if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
// {
// dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats");
// JObject dailyPNLDataJObject = dailyPNLDataJson as JObject;
// JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"];
// _dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList();
// _dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
// }
// }
// }
// return _dailyPNL;
// }
// }
public List<DailyPNLData> DailyPNL
{
get
@ -236,11 +271,52 @@ namespace Core.Main.DataObjects
{
if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
{
dynamic dailyPNLDataJson = GetDataFromProfitTrailer("/api/v2/data/stats");
JObject dailyPNLDataJObject = dailyPNLDataJson as JObject;
JArray dailyPNLSection = (JArray)dailyPNLDataJObject["extra"]["dailyPNLStats"];
_dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList();
_dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats"))
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader))
{
JObject basicSection = null;
JObject extraSection = null;
while (jsonReader.Read())
{
if (jsonReader.TokenType == JsonToken.PropertyName)
{
if ((string)jsonReader.Value == "basic")
{
jsonReader.Read(); // Move to the value of the "basic" property
basicSection = JObject.Load(jsonReader);
}
else if ((string)jsonReader.Value == "extra")
{
jsonReader.Read(); // Move to the value of the "extra" property
extraSection = JObject.Load(jsonReader);
}
}
if (basicSection != null && extraSection != null)
{
break;
}
}
if (basicSection != null &&
((_totalProfit == null ||
!Decimal.Equals(_totalProfit.Value, basicSection["totalProfit"].Value<decimal>())) ||
(_totalSales == null ||
!Decimal.Equals(_totalSales.Value, basicSection["totalSales"].Value<decimal>()))))
{
_totalProfit = basicSection["totalProfit"].Value<decimal>();
_totalSales = basicSection["totalSales"].Value<decimal>();
if (extraSection != null)
{
JArray dailyPNLSection = (JArray)extraSection["dailyPNLStats"];
_dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList();
_dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
}
}
}
}
}
}
@ -455,6 +531,23 @@ namespace Core.Main.DataObjects
return JArray.Parse(rawBody);
}
}
private Stream GetDataFromProfitTrailerAsStream(string callPath)
{
string url = string.Format("{0}{1}{2}token={3}", _systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL,
callPath,
callPath.Contains("?") ? "&" : "?",
_systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken);
// Get the data from PT
Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.AutomaticDecompression = DecompressionMethods.GZip;
request.KeepAlive = true;
WebResponse response = request.GetResponse();
return response.GetResponseStream();
}
private void BuildSellLogData(dynamic rawSellLogData)

View File

@ -14,12 +14,11 @@ namespace Core.Main
{
public class PTMagic
{
public PTMagic(LogHelper log)
{
this.Log = log;
}
#region Properties
private LogHelper _log;
private PTMagicConfiguration _systemConfiguration;
@ -84,7 +83,10 @@ namespace Core.Main
_systemConfiguration = value;
}
}
public class IsAnalzyerRunning
{
private string _isAnalyzerRunning;
}
public System.Timers.Timer Timer
{
get
@ -720,15 +722,6 @@ namespace Core.Main
this.Log.DoLogInfo("No CoinMarketCap API KEY specified! That's ok, but you can't use CoinMarketCap in your settings.analyzer.json");
}
// Check for CurrencyConverterApi Key
//if (!this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey.Equals(""))
//{
// this.Log.DoLogInfo("FreeCurrencyConverterApi KEY found");
//}
//else
//{
// this.Log.DoLogInfo("No FreeCurrencyConverterApi KEY specified. That's ok! But you can only use USD; apply for a key at: https://freecurrencyrates.com/en");
//}
}
catch (System.NullReferenceException)
{
@ -871,9 +864,6 @@ namespace Core.Main
// Check for latest GitHub version
this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version);
// Get latest main fiat currency exchange rate
// this.GetMainFiatCurrencyDetails();
// Load current PT files
this.LoadCurrentProfitTrailerProperties();

View File

@ -31,7 +31,7 @@ namespace Monitor.Pages
{
HttpContext.Session.SetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(), DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"));
PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = true;
PTMagicConfiguration.WriteGeneralSettings();
//PTMagicConfiguration.WriteGeneralSettings();
if (cbRememberMe != null)
{
if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase))

View File

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

View File

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

View File

@ -90,6 +90,7 @@ namespace Monitor.Pages
PTMagicConfiguration.GeneralSettings.Monitor.Port = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_Port"], PTMagicConfiguration.GeneralSettings.Monitor.Port);
PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_GraphIntervalMinutes"], PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes);
PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_GraphMaxTimeframeHours"], PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_ProfitsMaxTimeframeDays"], PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays);
PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_RefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds);
PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BagAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds);
PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BuyAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds);

View File

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

View File

@ -17,8 +17,7 @@ namespace Monitor.Pages
public StatsData StatsData { get; set; }
public PropertiesData PropertiesData { get; set; }
public SummaryData SummaryData { get; set; }
public List<DailyStatsData> DailyStats { get; set; } = new List<DailyStatsData>();
public List<DailyPNLData> DailyPNL { get; set; } = new List<DailyPNLData>();
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
public string TrendChartDataJSON = "";
public string ProfitChartDataJSON = "";
@ -42,7 +41,7 @@ namespace Monitor.Pages
StatsData = this.PTData.Stats;
PropertiesData = this.PTData.Properties;
SummaryData = this.PTData.Summary;
List<DailyStatsData> dailyStatsData = this.PTData.DailyStats;
List<DailyPNLData> dailyPNLData = this.PTData.DailyPNL;
// Cleanup temp files
@ -66,96 +65,103 @@ namespace Monitor.Pages
BuildProfitChartData();
}
private void BuildMarketTrendChartData()
{
StringBuilder trendChartDataJSON = new StringBuilder();
if (MarketTrends.Count > 0)
{
trendChartDataJSON.Append("[");
int mtIndex = 0;
foreach (MarketTrend mt in MarketTrends)
List<string> trendChartData = new List<string>();
if (MarketTrends.Count > 0)
{
if (mt.DisplayGraph)
int mtIndex = 0;
foreach (MarketTrend mt in MarketTrends)
{
string lineColor = "";
if (mtIndex < Constants.ChartLineColors.Length)
if (mt.DisplayGraph)
{
lineColor = Constants.ChartLineColors[mtIndex];
}
else
{
lineColor = Constants.ChartLineColors[mtIndex - 20];
}
string lineColor = mtIndex < Constants.ChartLineColors.Length
? Constants.ChartLineColors[mtIndex]
: Constants.ChartLineColors[mtIndex - 20];
if (Summary.MarketTrendChanges.ContainsKey(mt.Name))
{
List<MarketTrendChange> marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name];
if (marketTrendChangeSummaries.Count > 0)
if (Summary.MarketTrendChanges.ContainsKey(mt.Name))
{
if (!trendChartDataJSON.ToString().Equals("[")) trendChartDataJSON.Append(",");
List<MarketTrendChange> marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name];
trendChartDataJSON.Append("{");
trendChartDataJSON.Append("key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',");
trendChartDataJSON.Append("color: '" + lineColor + "',");
trendChartDataJSON.Append("values: [");
// Get trend ticks for chart
DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0);
DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
DateTime endDateTime = currentDateTime;
int trendChartTicks = 0;
for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes))
if (marketTrendChangeSummaries.Count > 0)
{
List<MarketTrendChange> tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList();
if (tickRange.Count > 0)
List<string> trendValues = new List<string>();
// Sort marketTrendChangeSummaries by TrendDateTime
marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList();
// Get trend ticks for chart
DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0);
DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
DateTime endDateTime = currentDateTime;
// Cache the result of SplitCamelCase(mt.Name)
string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name);
for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes))
{
MarketTrendChange mtc = tickRange.First();
if (tickTime != startDateTime) trendChartDataJSON.Append(",\n");
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0;
// Use binary search to find the range of items that match the condition
int index = marketTrendChangeSummaries.BinarySearch(new MarketTrendChange { TrendDateTime = tickTime }, Comparer<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;
trendChartDataJSON.Append("{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}");
trendChartTicks++;
trendValues.Add("{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
}
}
// Add most recent tick
MarketTrendChange latestMtc = marketTrendChangeSummaries.Last();
if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0;
trendValues.Add("{ x: new Date('" + latestMtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
// Use cached splitCamelCaseName
trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }");
mtIndex++;
}
// Add most recent tick
List<MarketTrendChange> latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList();
if (latestTickRange.Count > 0)
{
MarketTrendChange mtc = latestTickRange.First();
if (trendChartTicks > 0) trendChartDataJSON.Append(",\n");
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0;
trendChartDataJSON.Append("{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}");
}
trendChartDataJSON.Append("]");
trendChartDataJSON.Append("}");
mtIndex++;
}
}
}
}
trendChartDataJSON.Append("]");
TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]";
}
TrendChartDataJSON = trendChartDataJSON.ToString();
}
private void BuildProfitChartData()
{
StringBuilder profitPerDayJSON = new StringBuilder();
if (PTData.DailyStats.Count > 0)
if (PTData.DailyPNL.Count > 0)
{
DateTime endDate = DateTime.UtcNow.Date;
DateTime startDate = endDate.AddDays(-30);
DateTime startDate = endDate.AddDays(-PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays - 1); // Fetch data for timeframe + 1 days
double previousDayCumulativeProfit = 0;
bool isFirstDay = true;
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{
if (profitPerDayJSON.Length > 0)
DailyPNLData dailyPNL = PTData.DailyPNL.Find(ds => DateTime.ParseExact(ds.Date, "d-M-yyyy", CultureInfo.InvariantCulture) == date);
if (dailyPNL != null)
{
profitPerDayJSON.Append(",\n");
if (isFirstDay)
{
isFirstDay = false;
}
else
{
// Calculate the profit for the current day
double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2);
// Add the data point to the JSON string
if (profitPerDayJSON.Length > 0)
{
profitPerDayJSON.Append(",\n");
}
profitPerDayJSON.Append("{x: new Date('" + date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}");
}
previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency;
}
DailyStatsData dailyStats = PTData.DailyStats.Find(ds => DateTime.ParseExact(ds.Date, "d-M-yyyy", CultureInfo.InvariantCulture) == date);
double profitFiat = dailyStats != null ? Math.Round(dailyStats.TotalProfitCurrency,2) : 0;
profitPerDayJSON.Append("{x: new Date('" + date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}");
}
ProfitChartDataJSON = "[{key: 'Profit in " + PTData.Properties.Currency + "',color: '" + Constants.ChartLineColors[1] + "',values: [" + profitPerDayJSON.ToString() + "]}]";
}
@ -171,8 +177,8 @@ namespace Monitor.Pages
bool isSellStrategyTrue = false;
bool isTrailingSellActive = false;
foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog)
{
foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog)
{
string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive);
// Aggregate totals

View File

@ -23,8 +23,9 @@
"Port": 8080, // The port you want to run your monitor on
"RootUrl": "/", // The root Url of your monitor
"AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your default currency against USD. You can change that here. (eg., BTCEUR)
"GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points
"GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers
"GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points in minutes
"GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers in hours
"ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days
"RefreshSeconds": 30, // The refresh interval of your monitor main page
"LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
"MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart.

View File

@ -23,8 +23,9 @@
"Port": 8080, // The port you want to run your monitor on
"RootUrl": "/", // The root Url of your monitor
"AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your default currency against USD. You can change that here. (eg., BTCEUR)
"GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points
"GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers
"GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points in minutes
"GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers in hours
"ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days
"RefreshSeconds": 30, // The refresh interval of your monitor main page
"LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
"MaxSalesRecords": 99999, // The maximum number of sales records pulled from Profit Trailer. Changes require a Monitor Restart.

View File

@ -16,7 +16,7 @@
- This version of PT Magic only works with Profit Trailer v2.4.x and above
### .Net
- PTMagic requires .Net Core Runtime 3.1 on the host operating system (download: https://dotnet.microsoft.com/download)
- PTMagic requires .Net Core Runtime 7 on the host operating system (download: https://dotnet.microsoft.com/download)
### Settings
- **If you are updating from any version of PTMagic prior to 2.4.1,** check the default settings included with this release for any new lines that might need to be added to your settings.general.json file.