More Dashboard Bottom Optimizations
This commit is contained in:
parent
28c12e6d5a
commit
16fada1443
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -14,7 +14,6 @@ namespace Core.Main
|
|||
{
|
||||
public class PTMagic
|
||||
{
|
||||
|
||||
public PTMagic(LogHelper log)
|
||||
{
|
||||
this.Log = log;
|
||||
|
@ -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();
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue