Merge pull request #338 from PTMagicians/develop

2.7.1
This commit is contained in:
HojouFotytu 2024-01-23 01:46:57 +09:00 committed by GitHub
commit 111c1b153b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 2163 additions and 1216 deletions

View File

@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Text;
namespace Core.Main.DataObjects.PTMagicData namespace Core.Main.DataObjects.PTMagicData
{ {
#region Settings Objects
public class GeneralSettingsWrapper public class GeneralSettingsWrapper
{ {
public GeneralSettings GeneralSettings { get; set; } public GeneralSettings GeneralSettings { get; set; }
@ -21,7 +19,7 @@ namespace Core.Main.DataObjects.PTMagicData
public SecureSettings SecureSettings { get; set; } public SecureSettings SecureSettings { get; set; }
} }
#region GeneralSettings
public class GeneralSettings public class GeneralSettings
{ {
public Application Application { get; set; } public Application Application { get; set; }
@ -43,12 +41,10 @@ namespace Core.Main.DataObjects.PTMagicData
public string ProfitTrailerDefaultSettingName { get; set; } = "default"; public string ProfitTrailerDefaultSettingName { get; set; } = "default";
public int FloodProtectionMinutes { get; set; } = 15; public int FloodProtectionMinutes { get; set; } = 15;
public string Exchange { get; set; } public string Exchange { get; set; }
public double StartBalance { get; set; } = 0;
public string InstanceName { get; set; } = "PT Magic"; public string InstanceName { get; set; } = "PT Magic";
public string TimezoneOffset { get; set; } = "+0:00"; public string TimezoneOffset { get; set; } = "+0:00";
public string MainFiatCurrency { get; set; } = "USD";
public string CoinMarketCapAPIKey { get; set; } public string CoinMarketCapAPIKey { get; set; }
public string FreeCurrencyConverterAPIKey { get; set; } //public string FreeCurrencyConverterAPIKey { get; set; }
} }
public class Monitor public class Monitor
@ -61,9 +57,11 @@ namespace Core.Main.DataObjects.PTMagicData
public string AnalyzerChart { get; set; } = ""; public string AnalyzerChart { get; set; } = "";
public int GraphIntervalMinutes { get; set; } = 60; public int GraphIntervalMinutes { get; set; } = 60;
public int GraphMaxTimeframeHours { get; set; } = 24; public int GraphMaxTimeframeHours { get; set; } = 24;
public int ProfitsMaxTimeframeDays { get; set; } = 60;
public int RefreshSeconds { get; set; } = 30; public int RefreshSeconds { get; set; } = 30;
public int BagAnalyzerRefreshSeconds { get; set; } = 5; public int BagAnalyzerRefreshSeconds { get; set; } = 5;
public int BuyAnalyzerRefreshSeconds { get; set; } = 5; public int BuyAnalyzerRefreshSeconds { get; set; } = 5;
//public int MaxSalesRecords { get; set; } = 99999;
public int MaxTopMarkets { get; set; } = 20; public int MaxTopMarkets { get; set; } = 20;
public int MaxDailySummaries { get; set; } = 10; public int MaxDailySummaries { get; set; } = 10;
public int MaxMonthlySummaries { get; set; } = 10; public int MaxMonthlySummaries { get; set; } = 10;
@ -105,9 +103,7 @@ namespace Core.Main.DataObjects.PTMagicData
public Int64 ChatId { get; set; } public Int64 ChatId { get; set; }
public bool SilentMode { get; set; } = false; public bool SilentMode { get; set; } = false;
} }
#endregion
#region AnalyzerSettings
public class AnalyzerSettings public class AnalyzerSettings
{ {
public MarketAnalyzer MarketAnalyzer { get; set; } public MarketAnalyzer MarketAnalyzer { get; set; }
@ -130,6 +126,7 @@ namespace Core.Main.DataObjects.PTMagicData
[DefaultValue("Market")] [DefaultValue("Market")]
public string TrendCurrency { get; set; } = "Market"; public string TrendCurrency { get; set; } = "Market";
public string SplitCamelCaseName { get; set; }
[DefaultValue(0)] [DefaultValue(0)]
public int MaxMarkets { get; set; } = 0; public int MaxMarkets { get; set; } = 0;
@ -243,18 +240,12 @@ namespace Core.Main.DataObjects.PTMagicData
[DefaultValue(0)] [DefaultValue(0)]
public int HoursSinceTriggered { get; set; } = 0; public int HoursSinceTriggered { get; set; } = 0;
} }
#endregion
#region SecureSettings
public class SecureSettings public class SecureSettings
{ {
public string MonitorPassword { get; set; } = ""; public string MonitorPassword { get; set; } = "";
} }
#endregion
#endregion
#region Market Analyzer Objects
public class Market public class Market
{ {
public int Position; public int Position;
@ -290,9 +281,7 @@ namespace Core.Main.DataObjects.PTMagicData
public DateTime FirstSeen = Constants.confMinDate; public DateTime FirstSeen = Constants.confMinDate;
public DateTime LastSeen = Constants.confMaxDate; public DateTime LastSeen = Constants.confMaxDate;
} }
#endregion
#region Summary Objects
public class Summary public class Summary
{ {
public string Version { get; set; } = ""; public string Version { get; set; } = "";
@ -323,13 +312,25 @@ namespace Core.Main.DataObjects.PTMagicData
public string SellStrategy { get; set; } = ""; public string SellStrategy { get; set; } = "";
public string MainMarket { get; set; } = ""; public string MainMarket { get; set; } = "";
public double MainMarketPrice { get; set; } = 0; public double MainMarketPrice { get; set; } = 0;
public string MainFiatCurrency { get; set; } = "USD"; private PropertiesData _propertiesData = new PropertiesData();
public double MainFiatCurrencyExchangeRate { get; set; } = 1; public string MainFiatCurrency => _propertiesData.Currency;
private MiscData _miscData = new MiscData();
public double MainFiatCurrencyExchangeRate => _miscData.FiatConversionRate;
public List<StrategySummary> BuyStrategies { get; set; } = new List<StrategySummary>(); public List<StrategySummary> BuyStrategies { get; set; } = new List<StrategySummary>();
public List<StrategySummary> SellStrategies { get; set; } = new List<StrategySummary>(); public List<StrategySummary> SellStrategies { get; set; } = new List<StrategySummary>();
public List<StrategySummary> DCABuyStrategies { get; set; } = new List<StrategySummary>(); public List<StrategySummary> DCABuyStrategies { get; set; } = new List<StrategySummary>();
public List<StrategySummary> DCASellStrategies { get; set; } = new List<StrategySummary>(); public List<StrategySummary> DCASellStrategies { get; set; } = new List<StrategySummary>();
} }
public class PropertiesData
{
public string Currency { get; set; } = "";
public bool Shorting { get; set; } = false;
public bool Margin { get; set; } = false;
public string UpTime { get; set; } = "";
public int Port { get; set; } = 0;
public bool IsLeverageExchange { get; set; } = false;
public string BaseUrl { get; set; } = "";
}
public class StrategySummary public class StrategySummary
{ {
@ -363,22 +364,7 @@ namespace Core.Main.DataObjects.PTMagicData
public List<StrategySummary> DCABuyStrategies { get; set; } = new List<StrategySummary>(); public List<StrategySummary> DCABuyStrategies { get; set; } = new List<StrategySummary>();
public List<StrategySummary> DCASellStrategies { get; set; } = new List<StrategySummary>(); public List<StrategySummary> DCASellStrategies { get; set; } = new List<StrategySummary>();
} }
#endregion
#region Properties Objects
public class Properties
{
public string Currency { get; set; } = "";
public bool Shorting { get; set; } = false;
public bool Margin { get; set; } = false;
public string UpTime { get; set; } = "";
public int Port { get; set; } = 0;
public bool IsLeverageExchange { get; set; } = false;
public string BaseUrl { get; set; } = "";
}
#endregion
#region Transaction Objects
public class Transaction public class Transaction
{ {
public string GUID { get; set; } = ""; public string GUID { get; set; } = "";
@ -396,9 +382,7 @@ namespace Core.Main.DataObjects.PTMagicData
return result.DateTime; return result.DateTime;
} }
} }
#endregion
#region SingleMarketSettingSummary Objects
public class SingleMarketSettingSummary public class SingleMarketSettingSummary
{ {
public string Market { get; set; } = ""; public string Market { get; set; } = "";
@ -414,24 +398,93 @@ namespace Core.Main.DataObjects.PTMagicData
public double LastPrice { get; set; } = 0; public double LastPrice { get; set; } = 0;
public double Last24hVolume { get; set; } = 0; public double Last24hVolume { get; set; } = 0;
} }
#endregion
#region Profit Trailer JSON Objects // public class SellLogData
// {
// public double SoldAmount { get; set; }
// public DateTime SoldDate { get; set; }
// public int BoughtTimes { get; set; }
// public string Market { get; set; }
// public double ProfitPercent { get; set; }
// public double Profit { get; set; }
// public double AverageBuyPrice { get; set; }
// public double TotalCost { get; set; }
// public double SoldPrice { get; set; }
// public double SoldValue { get; set; }
// public double TotalSales { get; set; }
// }
public class SellLogData public class StatsData
{ {
public double SoldAmount { get; set; } public double SalesToday { get; set; }
public DateTime SoldDate { get; set; } public double ProfitToday { get; set; }
public int BoughtTimes { get; set; } public double ProfitPercToday { get; set; }
public string Market { get; set; } public double SalesYesterday { get; set; }
public double ProfitPercent { get; set; } public double ProfitYesterday { get; set; }
public double Profit { get; set; } public double ProfitPercYesterday { get; set; }
public double AverageBuyPrice { get; set; } public double SalesWeek { get; set; }
public double TotalCost { get; set; } public double ProfitWeek { get; set; }
public double SoldPrice { get; set; } public double ProfitPercWeek { get; set; }
public double SoldValue { get; set; } public double SalesThisMonth { get; set; }
public double ProfitThisMonth { get; set; }
public double ProfitPercThisMonth { get; set; }
public double SalesLastMonth { get; set; }
public double ProfitLastMonth { get; set; }
public double ProfitPercLastMonth { get; set; }
public double TotalProfit { get; set; }
public double TotalSales { get; set; }
public double TotalProfitPerc { get; set; }
public double FundingToday { get; set; }
public double FundingYesterday { get; set; }
public double FundingWeek { get; set; }
public double FundingThisMonth { get; set; }
public double FundingLastMonth { get; set; }
public double FundingTotal { get; set; }
public double TotalFundingPerc { get; set; }
public double TotalFundingPercYesterday { get; set; }
public double TotalFundingPercWeek { get; set; }
public double TotalFundingPercToday { get; set; }
} }
public class DailyPNLData
{
public string Date { get; set; }
public double CumulativeProfitCurrency { get; set; }
public double Order { get; set; }
}
public class DailyTCVData
{
public string Date { get; set; }
public double TCV { get; set; }
public double Order { get; set; }
}
public class MonthlyStatsData
{
public string Month { get; set; }
public double TotalProfitCurrency { get; set; }
public double TotalSales { get; set; }
public double AvgGrowth { get; set; }
public double Order { get; set; }
}
public class ProfitablePairsData
{
public string Coin { get; set; }
public double ProfitCurrency { get; set; }
public int SoldTimes { 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; }
@ -507,16 +560,17 @@ namespace Core.Main.DataObjects.PTMagicData
public List<Strategy> BuyStrategies { get; set; } = new List<Strategy>(); public List<Strategy> BuyStrategies { get; set; } = new List<Strategy>();
} }
public class SummaryData public class MiscData
{ {
public double Balance { get; set; } public double Balance { get; set; }
public double StartBalance { get; set; } public double StartBalance { get; set; }
public double FiatConversionRate { get; set; }
public double PairsValue { get; set; } public double PairsValue { get; set; }
public double DCAValue { get; set; } public double DCAValue { get; set; }
public double PendingValue { get; set; } public double PendingValue { get; set; }
public double DustValue { get; set; } public double DustValue { get; set; }
public string Market { get; set; } public string Market { get; set; }
public string TotalCurrentValue { get; set; }
public string TimeZoneOffset { get; set; }
} }
#endregion
} }

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Diagnostics; using System.Diagnostics;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
@ -14,18 +15,60 @@ namespace Core.Main.DataObjects
public class ProfitTrailerData public class ProfitTrailerData
{ {
private SummaryData _summary = null; private MiscData _misc = null;
private Properties _properties = null; private PropertiesData _properties = null;
private List<SellLogData> _sellLog = new List<SellLogData>(); private StatsData _stats = null;
private List<DailyPNLData> _dailyPNL = new List<DailyPNLData>();
private List<DailyTCVData> _dailyTCV = new List<DailyTCVData>();
private List<MonthlyStatsData> _monthlyStats = new List<MonthlyStatsData>();
private List<ProfitablePairsData> _profitablePairs = new List<ProfitablePairsData>();
private List<DailyStatsData> _dailyStats = new List<DailyStatsData>();
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<DCALogData> _dcaLog = new List<DCALogData>();
private List<BuyLogData> _buyLog = new List<BuyLogData>(); private List<BuyLogData> _buyLog = new List<BuyLogData>();
private string _ptmBasePath = ""; private string _ptmBasePath = "";
private PTMagicConfiguration _systemConfiguration = null; private PTMagicConfiguration _systemConfiguration = null;
private TransactionData _transactionData = null; private TransactionData _transactionData = null;
private DateTime _buyLogRefresh = DateTime.UtcNow, _sellLogRefresh = DateTime.UtcNow, _dcaLogRefresh = DateTime.UtcNow, _summaryRefresh = DateTime.UtcNow, _propertiesRefresh = DateTime.UtcNow; private DateTime _dailyPNLRefresh = DateTime.UtcNow;
private volatile object _buyLock = new object(), _sellLock = new object(), _dcaLock = new object(), _summaryLock = new object(), _propertiesLock = new object(); private DateTime _dailyTCVRefresh = DateTime.UtcNow;
private DateTime _monthlyStatsRefresh = DateTime.UtcNow;
private DateTime _statsRefresh = DateTime.UtcNow;
private DateTime _buyLogRefresh = DateTime.UtcNow;
//private DateTime _sellLogRefresh = DateTime.UtcNow;
private DateTime _dcaLogRefresh = DateTime.UtcNow;
private DateTime _miscRefresh = DateTime.UtcNow;
private DateTime _propertiesRefresh = DateTime.UtcNow;
private DateTime _profitablePairsRefresh = DateTime.UtcNow;
private DateTime _dailyStatsRefresh = DateTime.UtcNow;
private volatile object _dailyPNLLock = new object();
private volatile object _dailyTCVLock = new object();
private volatile object _monthlyStatsLock = new object();
private volatile object _statsLock = new object();
private volatile object _buyLock = new object();
private volatile object _sellLock = new object();
private volatile object _dcaLock = new object();
private volatile object _miscLock = new object();
private volatile object _propertiesLock = 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)
{
// Implement your logging logic here
Console.WriteLine(message);
}
// Constructor // Constructor
public ProfitTrailerData(PTMagicConfiguration systemConfiguration) public ProfitTrailerData(PTMagicConfiguration systemConfiguration)
{ {
@ -56,27 +99,111 @@ namespace Core.Main.DataObjects
} }
} }
public SummaryData Summary public MiscData Misc
{ {
get get
{ {
if (_summary == null || (DateTime.UtcNow > _summaryRefresh)) if (_misc == null || (DateTime.UtcNow > _miscRefresh))
{ {
lock (_summaryLock) lock (_miscLock)
{ {
// Thread double locking // Thread double locking
if (_summary == null || (DateTime.UtcNow > _summaryRefresh)) if (_misc == null || (DateTime.UtcNow > _miscRefresh))
{ {
_summary = BuildSummaryData(GetDataFromProfitTrailer("api/v2/data/misc")); _misc = BuildMiscData(GetDataFromProfitTrailer("api/v2/data/misc"));
_summaryRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); _miscRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
} }
} }
} }
return _summary; return _misc;
} }
} }
public Properties Properties private MiscData BuildMiscData(dynamic PTData)
{
return new MiscData()
{
Market = PTData.market,
FiatConversionRate = PTData.priceDataFiatConversionRate,
Balance = PTData.realBalance,
PairsValue = PTData.totalPairsCurrentValue,
DCAValue = PTData.totalDCACurrentValue,
PendingValue = PTData.totalPendingCurrentValue,
DustValue = PTData.totalDustCurrentValue,
StartBalance = PTData.startBalance,
TotalCurrentValue = PTData.totalCurrentValue,
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
{ {
get get
{ {
@ -96,81 +223,420 @@ namespace Core.Main.DataObjects
return _properties; return _properties;
} }
} }
public List<SellLogData> SellLog private PropertiesData BuildProptertiesData(dynamic PTProperties)
{
return new PropertiesData()
{
Currency = PTProperties.currency,
Shorting = PTProperties.shorting,
Margin = PTProperties.margin,
UpTime = PTProperties.upTime,
Port = PTProperties.port,
IsLeverageExchange = PTProperties.isLeverageExchange,
BaseUrl = PTProperties.baseUrl
};
}
public StatsData Stats
{ {
get get
{ {
if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) if (_stats == null || DateTime.UtcNow > _statsRefresh)
{ {
lock (_sellLock) lock (_statsLock)
{ {
// Thread double locking if (_stats == null || DateTime.UtcNow > _statsRefresh)
if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
{ {
_sellLog.Clear(); using (var stream = GetDataFromProfitTrailerAsStream("/api/v2/data/stats"))
using (var reader = new StreamReader(stream))
// Page through the sales data summarizing it. using (var jsonReader = new JsonTextReader(reader))
bool exitLoop = false;
int pageIndex = 1;
while (!exitLoop)
{ {
var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?perPage=5000&sort=SOLDDATE&sortDirection=ASCENDING&page=" + pageIndex); while (jsonReader.Read())
if (sellDataPage != null && sellDataPage.data.Count > 0)
{ {
// Add sales data page to collection if (jsonReader.TokenType == JsonToken.PropertyName && (string)jsonReader.Value == "basic")
this.BuildSellLogData(sellDataPage);
pageIndex++;
}
else
{ {
// All data retrieved jsonReader.Read(); // Move to the value of the "basic" property
exitLoop = true; JObject basicSection = JObject.Load(jsonReader);
} _stats = BuildStatsData(basicSection);
} _statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
break;
// Update sell log refresh time
_sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
} }
} }
} }
return _sellLog;
} }
} }
}
public List<SellLogData> SellLogToday return _stats;
}
}
private StatsData BuildStatsData(dynamic statsDataJson)
{
return new StatsData()
{
SalesToday = statsDataJson["totalSalesToday"],
ProfitToday = statsDataJson["totalProfitToday"],
ProfitPercToday = statsDataJson["totalProfitPercToday"],
SalesYesterday = statsDataJson["totalSalesYesterday"],
ProfitYesterday = statsDataJson["totalProfitYesterday"],
ProfitPercYesterday = statsDataJson["totalProfitPercYesterday"],
SalesWeek = statsDataJson["totalSalesWeek"],
ProfitWeek = statsDataJson["totalProfitWeek"],
ProfitPercWeek = statsDataJson["totalProfitPercWeek"],
SalesThisMonth = statsDataJson["totalSalesThisMonth"],
ProfitThisMonth = statsDataJson["totalProfitThisMonth"],
ProfitPercThisMonth = statsDataJson["totalProfitPercThisMonth"],
SalesLastMonth = statsDataJson["totalSalesLastMonth"],
ProfitLastMonth = statsDataJson["totalProfitLastMonth"],
ProfitPercLastMonth = statsDataJson["totalProfitPercLastMonth"],
TotalProfit = statsDataJson["totalProfit"],
TotalSales = statsDataJson["totalSales"],
TotalProfitPerc = statsDataJson["totalProfitPerc"],
FundingToday = statsDataJson["totalFundingToday"],
FundingYesterday = statsDataJson["totalFundingYesterday"],
FundingWeek = statsDataJson["totalFundingWeek"],
FundingThisMonth = statsDataJson["totalFundingThisMonth"],
FundingLastMonth = statsDataJson["totalFundingLastMonth"],
FundingTotal = statsDataJson["totalFunding"],
TotalFundingPerc = statsDataJson["totalFundingPerc"],
TotalFundingPercYesterday = statsDataJson["totalFundingPercYesterday"],
TotalFundingPercWeek = statsDataJson["totalFundingPercWeekPerc"],
TotalFundingPercToday = statsDataJson["totalFundingPercTodayPerc"]
};
}
public List<DailyPNLData> DailyPNL
{ {
get get
{ {
return SellLog.FindAll(sl => sl.SoldDate.Date == LocalizedTime.DateTime.Date); if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
{
lock (_dailyPNLLock)
{
if (_dailyPNL == null || DateTime.UtcNow > _dailyPNLRefresh)
{
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);
} }
} }
public List<SellLogData> SellLogYesterday if (basicSection != null && extraSection != null)
{ {
get break;
{
return SellLog.FindAll(sl => sl.SoldDate.Date == LocalizedTime.DateTime.AddDays(-1).Date);
} }
} }
public List<SellLogData> SellLogLast7Days 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);
}
}
}
}
}
}
return _dailyPNL;
}
}
private DailyPNLData BuildDailyPNLData(dynamic dailyPNLDataJson)
{
return new DailyPNLData()
{
Date = dailyPNLDataJson["date"],
CumulativeProfitCurrency = dailyPNLDataJson["cumulativeProfitCurrency"],
Order = dailyPNLDataJson["order"],
};
}
public List<ProfitablePairsData> ProfitablePairs
{ {
get get
{ {
return SellLog.FindAll(sl => sl.SoldDate.Date >= LocalizedTime.DateTime.AddDays(-7).Date); if (_profitablePairs == null || DateTime.UtcNow > _profitablePairsRefresh)
{
lock (_profitablePairsLock)
{
if (_profitablePairs == null || DateTime.UtcNow > _profitablePairsRefresh)
{
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);
} }
} }
public List<SellLogData> SellLogLast30Days if (basicSection != null && extraSection != null)
{
break;
}
}
if (basicSection != null)
{
if (extraSection != null)
{
JObject profitablePairsSection = (JObject)extraSection["profitablePairs"];
_profitablePairs = new List<ProfitablePairsData>();
int counter = 0;
foreach (var j in profitablePairsSection)
{
if (counter >= _systemConfiguration.GeneralSettings.Monitor.MaxTopMarkets)
{
break;
}
// Process each JObject in the dictionary
JObject profitablePair = (JObject)j.Value;
_profitablePairs.Add(BuildProfitablePairs(profitablePair));
counter++;
}
_profitablePairsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
}
}
}
}
}
}
return _profitablePairs;
}
}
private ProfitablePairsData BuildProfitablePairs(JObject profitablePairsJson)
{
return new ProfitablePairsData()
{
Coin = profitablePairsJson["coin"].Value<string>(),
ProfitCurrency = profitablePairsJson["profitCurrency"].Value<double>(),
SoldTimes = profitablePairsJson["soldTimes"].Value<int>(),
Avg = profitablePairsJson["avg"].Value<double>(),
};
}
public List<DailyTCVData> DailyTCV
{ {
get get
{ {
return SellLog.FindAll(sl => sl.SoldDate.Date >= LocalizedTime.DateTime.AddDays(-30).Date); if (_dailyTCV == null || DateTime.UtcNow > _dailyTCVRefresh)
{
lock (_dailyTCVLock)
{
if (_dailyTCV == null || DateTime.UtcNow > _dailyTCVRefresh)
{
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 dailyTCVSection = (JArray)extraSection["dailyTCVStats"];
_dailyTCV = dailyTCVSection.Select(j => BuildDailyTCVData(j as JObject)).ToList();
_dailyTCVRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
}
}
}
}
}
}
return _dailyTCV;
}
}
public int GetTotalTCVDays()
{
return DailyTCV?.Count ?? 0;
}
private DailyTCVData BuildDailyTCVData(dynamic dailyTCVDataJson)
{
return new DailyTCVData()
{
Date = dailyTCVDataJson["date"],
TCV = dailyTCVDataJson["TCV"],
Order = dailyTCVDataJson["order"],
};
}
public List<MonthlyStatsData> MonthlyStats
{
get
{
if (_monthlyStats == null || DateTime.UtcNow > _monthlyStatsRefresh)
{
lock (_monthlyStatsLock)
{
if (_monthlyStats == null || DateTime.UtcNow > _monthlyStatsRefresh)
{
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 monthlyStatsSection = (JArray)extraSection["monthlyStats"];
_monthlyStats = monthlyStatsSection.Select(j => BuildMonthlyStatsData(j as JObject)).ToList();
_monthlyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
}
}
}
}
}
}
return _monthlyStats;
}
}
private MonthlyStatsData BuildMonthlyStatsData(dynamic monthlyStatsDataJson)
{
return new MonthlyStatsData()
{
Month = monthlyStatsDataJson["month"],
TotalSales = monthlyStatsDataJson["totalSales"],
TotalProfitCurrency = monthlyStatsDataJson["totalProfitCurrency"],
AvgGrowth = monthlyStatsDataJson["avgGrowth"],
Order = monthlyStatsDataJson["order"],
};
}
// public List<SellLogData> SellLog
// {
// get
// {
// if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
// {
// lock (_sellLock)
// {
// // Thread double locking
// if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
// {
// _sellLog.Clear();
// // Page through the sales data summarizing it.
// bool exitLoop = false;
// int pageIndex = 1;
// // 1 record per page to allow user to set max records to retrieve
// int maxPages = _systemConfiguration.GeneralSettings.Monitor.MaxSalesRecords;
// int requestedPages = 0;
// while (!exitLoop && requestedPages < maxPages)
// {
// var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?Page=1&perPage=1&sort=SOLDDATE&sortDirection=DESCENDING&page=" + pageIndex);
// if (sellDataPage != null && sellDataPage.data.Count > 0)
// {
// // Add sales data page to collection
// this.BuildSellLogData(sellDataPage);
// pageIndex++;
// requestedPages++;
// Console.WriteLine($"Importing salesLog: {pageIndex}");
// }
// else
// {
// // All data retrieved
// exitLoop = true;
// }
// }
// // Update sell log refresh time
// _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds -1);
// }
// }
// }
// return _sellLog;
// }
// }
public List<DCALogData> DCALog public List<DCALogData> DCALog
{ {
get get
@ -196,7 +662,6 @@ namespace Core.Main.DataObjects
() => () =>
{ {
pendingData = GetDataFromProfitTrailer("/api/v2/data/pending", true); pendingData = GetDataFromProfitTrailer("/api/v2/data/pending", true);
}, },
() => () =>
{ {
@ -247,41 +712,42 @@ namespace Core.Main.DataObjects
public double GetCurrentBalance() public double GetCurrentBalance()
{ {
return return
(this.Summary.Balance); (this.Misc.Balance);
} }
public double GetPairsBalance() public double GetPairsBalance()
{ {
return return
(this.Summary.PairsValue); (this.Misc.PairsValue);
} }
public double GetDCABalance() public double GetDCABalance()
{ {
return return
(this.Summary.DCAValue); (this.Misc.DCAValue);
} }
public double GetPendingBalance() public double GetPendingBalance()
{ {
return return
(this.Summary.PendingValue); (this.Misc.PendingValue);
} }
public double GetDustBalance() public double GetDustBalance()
{ {
return return
(this.Summary.DustValue); (this.Misc.DustValue);
} }
public double GetSnapshotBalance(DateTime snapshotDateTime)
{
double result = _systemConfiguration.GeneralSettings.Application.StartBalance;
result += this.SellLog.FindAll(sl => sl.SoldDate.Date < snapshotDateTime.Date).Sum(sl => sl.Profit); // public double GetSnapshotBalance(DateTime snapshotDateTime)
result += this.TransactionData.Transactions.FindAll(t => t.UTCDateTime < snapshotDateTime).Sum(t => t.Amount); // {
// double result = _misc.StartBalance;
// Calculate holdings for snapshot date // result += this.SellLog.FindAll(sl => sl.SoldDate.Date < snapshotDateTime.Date).Sum(sl => sl.Profit);
result += this.DCALog.FindAll(pairs => pairs.FirstBoughtDate <= snapshotDateTime).Sum(pairs => pairs.CurrentValue); // result += this.TransactionData.Transactions.FindAll(t => t.UTCDateTime < snapshotDateTime).Sum(t => t.Amount);
return result; // // Calculate holdings for snapshot date
} // result += this.DCALog.FindAll(pairs => pairs.FirstBoughtDate <= snapshotDateTime).Sum(pairs => pairs.CurrentValue);
// return result;
// }
private dynamic GetDataFromProfitTrailer(string callPath, bool arrayReturned = false) private dynamic GetDataFromProfitTrailer(string callPath, bool arrayReturned = false)
{ {
@ -308,7 +774,7 @@ namespace Core.Main.DataObjects
response.Close(); response.Close();
// Parse the JSON and build the data sets
if (!arrayReturned) if (!arrayReturned)
{ {
return JObject.Parse(rawBody); return JObject.Parse(rawBody);
@ -318,62 +784,58 @@ namespace Core.Main.DataObjects
return JArray.Parse(rawBody); 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);
private SummaryData BuildSummaryData(dynamic PTData) // Get the data from PT
{ Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url));
return new SummaryData() HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
{ request.AutomaticDecompression = DecompressionMethods.GZip;
Market = PTData.market, request.KeepAlive = true;
Balance = PTData.realBalance,
PairsValue = PTData.totalPairsCurrentValue, WebResponse response = request.GetResponse();
DCAValue = PTData.totalDCACurrentValue,
PendingValue = PTData.totalPendingCurrentValue, return response.GetResponseStream();
DustValue = PTData.totalDustCurrentValue
};
} }
private Properties BuildProptertiesData(dynamic PTProperties)
{
return new Properties()
{
Currency = PTProperties.currency,
Shorting = PTProperties.shorting,
Margin = PTProperties.margin,
UpTime = PTProperties.upTime,
Port = PTProperties.port,
IsLeverageExchange = PTProperties.isLeverageExchange,
BaseUrl = PTProperties.baseUrl
};
}
private void BuildSellLogData(dynamic rawSellLogData)
{
foreach (var rsld in rawSellLogData.data)
{
SellLogData sellLogData = new SellLogData();
sellLogData.SoldAmount = rsld.soldAmount;
sellLogData.BoughtTimes = rsld.boughtTimes;
sellLogData.Market = rsld.market;
sellLogData.ProfitPercent = rsld.profit;
sellLogData.SoldPrice = rsld.currentPrice;
sellLogData.AverageBuyPrice = rsld.avgPrice;
sellLogData.TotalCost = rsld.totalCost;
sellLogData.Profit = rsld.profitCurrency;
//Convert Unix Timestamp to Datetime // private void BuildSellLogData(dynamic rawSellLogData)
System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc); // {
dtDateTime = dtDateTime.AddSeconds((double)rsld.soldDate).ToUniversalTime(); // foreach (var rsld in rawSellLogData.data)
// {
// SellLogData sellLogData = new SellLogData();
// sellLogData.SoldAmount = rsld.soldAmount;
// sellLogData.BoughtTimes = rsld.boughtTimes;
// sellLogData.Market = rsld.market;
// sellLogData.ProfitPercent = rsld.profit;
// sellLogData.SoldPrice = rsld.currentPrice;
// sellLogData.AverageBuyPrice = rsld.avgPrice;
// sellLogData.TotalCost = rsld.totalCost;
// sellLogData.Profit = rsld.profitCurrency;
// Profit Trailer sales are saved in UTC
DateTimeOffset ptSoldDate = DateTimeOffset.Parse(dtDateTime.Year.ToString() + "-" + dtDateTime.Month.ToString("00") + "-" + dtDateTime.Day.ToString("00") + "T" + dtDateTime.Hour.ToString("00") + ":" + dtDateTime.Minute.ToString("00") + ":" + dtDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
// Convert UTC sales time to local offset time // //Convert Unix Timestamp to Datetime
ptSoldDate = ptSoldDate.ToOffset(OffsetTimeSpan); // System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
// dtDateTime = dtDateTime.AddSeconds((double)rsld.soldDate).ToUniversalTime();
sellLogData.SoldDate = ptSoldDate.DateTime;
_sellLog.Add(sellLogData); // // Profit Trailer sales are saved in UTC
} // DateTimeOffset ptSoldDate = DateTimeOffset.Parse(dtDateTime.Year.ToString() + "-" + dtDateTime.Month.ToString("00") + "-" + dtDateTime.Day.ToString("00") + "T" + dtDateTime.Hour.ToString("00") + ":" + dtDateTime.Minute.ToString("00") + ":" + dtDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}
// // Convert UTC sales time to local offset time
// ptSoldDate = ptSoldDate.ToOffset(OffsetTimeSpan);
// sellLogData.SoldDate = ptSoldDate.DateTime;
// _sellLog.Add(sellLogData);
// }
// }
private void BuildDCALogData(dynamic rawDCALogData, dynamic rawPairsLogData, dynamic rawPendingLogData, dynamic rawWatchModeLogData) private void BuildDCALogData(dynamic rawDCALogData, dynamic rawPairsLogData, dynamic rawPendingLogData, dynamic rawWatchModeLogData)
{ {
@ -388,7 +850,6 @@ namespace Core.Main.DataObjects
// Parse watch only pairs data // Parse watch only pairs data
_dcaLog.AddRange(ParsePairsData(rawWatchModeLogData, false)); _dcaLog.AddRange(ParsePairsData(rawWatchModeLogData, false));
} }
// Parse the pairs data from PT to our own common data structure. // Parse the pairs data from PT to our own common data structure.

View File

@ -14,7 +14,6 @@ namespace Core.Main
{ {
public class PTMagic public class PTMagic
{ {
public PTMagic(LogHelper log) public PTMagic(LogHelper log)
{ {
this.Log = log; this.Log = log;
@ -84,7 +83,10 @@ namespace Core.Main
_systemConfiguration = value; _systemConfiguration = value;
} }
} }
public class IsAnalzyerRunning
{
private string _isAnalyzerRunning;
}
public System.Timers.Timer Timer public System.Timers.Timer Timer
{ {
get 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"); 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) catch (System.NullReferenceException)
{ {
@ -871,9 +864,6 @@ namespace Core.Main
// Check for latest GitHub version // Check for latest GitHub version
this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version); this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version);
// Get latest main fiat currency exchange rate
this.GetMainFiatCurrencyDetails();
// Load current PT files // Load current PT files
this.LoadCurrentProfitTrailerProperties(); this.LoadCurrentProfitTrailerProperties();
@ -1127,35 +1117,6 @@ namespace Core.Main
} }
} }
private void GetMainFiatCurrencyDetails()
{
this.LastRuntimeSummary.MainFiatCurrency = this.LastMainFiatCurrency;
this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = this.LastMainFiatCurrencyExchangeRate;
if (this.LastFiatCurrencyCheck < DateTime.UtcNow.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase))
{
try
{
this.LastRuntimeSummary.MainFiatCurrency = this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency;
this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = BaseAnalyzer.GetMainFiatCurrencyRate(this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency, this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey, this.Log);
this.LastMainFiatCurrency = this.LastRuntimeSummary.MainFiatCurrency;
this.LastMainFiatCurrencyExchangeRate = this.LastRuntimeSummary.MainFiatCurrencyExchangeRate;
this.LastFiatCurrencyCheck = DateTime.UtcNow;
}
catch (Exception ex)
{
// Fallback to USD in case something went wrong
this.Log.DoLogError("Fixer.io exchange rate check error: " + ex.Message);
this.LastRuntimeSummary.MainFiatCurrency = "USD";
this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = 1;
this.LastMainFiatCurrency = "USD";
this.LastMainFiatCurrencyExchangeRate = 1;
}
}
}
// Get current PT properties // Get current PT properties
private void LoadCurrentProfitTrailerProperties() private void LoadCurrentProfitTrailerProperties()
{ {

View File

@ -437,7 +437,7 @@ namespace Core.ProfitTrailer
} }
else else
{ {
strategyText += "<span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"CONDITIONAL FORMULA\">FORM</span> "; strategyText += "";
} }
} }
else else

View File

@ -64,6 +64,8 @@
var loadDashboardBottom = function () { var loadDashboardBottom = function () {
//destroy all d3 svg graph to avoid memory leak //destroy all d3 svg graph to avoid memory leak
setTimeout(function()
{
$(".nvtooltip").remove(); $(".nvtooltip").remove();
$("svg > *").remove(); $("svg > *").remove();
$("svg").remove(); $("svg").remove();
@ -71,6 +73,7 @@
nv.graphs = []; nv.graphs = [];
nv.logs = {}; nv.logs = {};
nv.tooltip = {}; nv.tooltip = {};
}, 10 * intervalDashboardBottom * 1000); // 10 times intervalDashboardBottom in milliseconds
// Clear exisitng interval to stop multiple attempts to load at the same time. // Clear exisitng interval to stop multiple attempts to load at the same time.
if (intervalDashboardBottom != null) if (intervalDashboardBottom != null)

View File

@ -28,7 +28,6 @@
</button> </button>
</span> </span>
</div> </div>
<p><input type="checkbox" name="cbRememberMe"/> <label for="cbRememberMe">Stay logged in</label></p>
</div> </div>
</form> </form>

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'")); 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.GeneralSettings.Monitor.IsPasswordProtected = true;
PTMagicConfiguration.WriteGeneralSettings(); //PTMagicConfiguration.WriteGeneralSettings();
if (cbRememberMe != null) if (cbRememberMe != null)
{ {
if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase)) if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase))

View File

@ -155,14 +155,14 @@ else
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 m-b-20 header-title"><b>Market Trend Averages at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange</b></h4> <h4 class="m-t-0 m-b-20 header-title"><b>Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange </b><i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="@Math.Round(Model.DataHours, 1) hours of data available. Currently set to show @Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours hours at @Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes intervals, in general settings."></i></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">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>
@ -190,9 +190,9 @@ else
} }
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 bold-text" >@trendChangeOutput%</td>
</tr> </tr>
} }
} }

View File

@ -4,6 +4,7 @@ using System.Linq;
using Core.Main; using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
using System.Globalization;
namespace Monitor.Pages namespace Monitor.Pages
{ {
@ -11,6 +12,7 @@ namespace Monitor.Pages
{ {
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>(); public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
public string TrendChartDataJSON = ""; public string TrendChartDataJSON = "";
public double DataHours { get; set; }
public void OnGet() public void OnGet()
{ {
@ -28,23 +30,18 @@ namespace Monitor.Pages
private void BuildMarketTrendChartData() private void BuildMarketTrendChartData()
{ {
List<string> trendChartData = new List<string>();
if (MarketTrends.Count > 0) if (MarketTrends.Count > 0)
{ {
TrendChartDataJSON = "[";
int mtIndex = 0; int mtIndex = 0;
foreach (MarketTrend mt in MarketTrends) foreach (MarketTrend mt in MarketTrends)
{ {
if (mt.DisplayGraph) if (mt.DisplayGraph)
{ {
string lineColor = ""; string lineColor = mtIndex < Constants.ChartLineColors.Length
if (mtIndex < Constants.ChartLineColors.Length) ? Constants.ChartLineColors[mtIndex]
{ : Constants.ChartLineColors[mtIndex - 20];
lineColor = Constants.ChartLineColors[mtIndex];
}
else
{
lineColor = Constants.ChartLineColors[mtIndex - 20];
}
if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) if (Summary.MarketTrendChanges.ContainsKey(mt.Name))
{ {
@ -52,53 +49,66 @@ namespace Monitor.Pages
if (marketTrendChangeSummaries.Count > 0) if (marketTrendChangeSummaries.Count > 0)
{ {
if (!TrendChartDataJSON.Equals("[")) TrendChartDataJSON += ","; List<string> trendValues = new List<string>();
TrendChartDataJSON += "{"; // Sort marketTrendChangeSummaries by TrendDateTime
TrendChartDataJSON += "key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',"; marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList();
TrendChartDataJSON += "color: '" + lineColor + "',";
TrendChartDataJSON += "values: [";
// Get trend ticks for chart // Get trend ticks for chart
DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); TimeSpan offset;
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
{
offset = TimeSpan.Zero; // If offset is invalid, set it to zero
}
DateTime currentDateTime = DateTime.UtcNow;
DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
DateTime endDateTime = currentDateTime; DateTime endDateTime = currentDateTime;
int trendChartTicks = 0;
// Ensure startDateTime doesn't exceed the available data
DateTime earliestTrendDateTime = marketTrendChangeSummaries.Min(mtc => mtc.TrendDateTime);
startDateTime = startDateTime > earliestTrendDateTime ? startDateTime : earliestTrendDateTime;
DataHours = (currentDateTime - earliestTrendDateTime).TotalHours;
// Cache the result of SplitCamelCase(mt.Name)
string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name);
for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes))
{ {
List<MarketTrendChange> tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); // Use binary search to find the range of items that match the condition
if (tickRange.Count > 0) 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 = tickRange.First(); MarketTrendChange mtc = marketTrendChangeSummaries[index];
if (tickTime != startDateTime) TrendChartDataJSON += ",\n";
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0;
TrendChartDataJSON += "{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; // Adjust tickTime to the desired timezone before converting to string
trendChartTicks++; DateTime adjustedTickTime = tickTime.Add(isNegative ? -offset : offset);
trendValues.Add("{ x: new Date('" + adjustedTickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
} }
} }
// Add most recent tick // Add most recent tick
List<MarketTrendChange> latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); MarketTrendChange latestMtc = marketTrendChangeSummaries.Last();
if (latestTickRange.Count > 0) if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0;
{
MarketTrendChange mtc = latestTickRange.First();
if (trendChartTicks > 0) TrendChartDataJSON += ",\n";
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0;
TrendChartDataJSON += "{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; // Adjust latestMtc.TrendDateTime to the desired timezone before converting to string
} DateTime adjustedLatestTrendDateTime = latestMtc.TrendDateTime.Add(isNegative ? -offset : offset);
trendValues.Add("{ x: new Date('" + adjustedLatestTrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
TrendChartDataJSON += "]";
TrendChartDataJSON += "}";
// Use cached splitCamelCaseName
trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }");
mtIndex++; mtIndex++;
} }
} }
} }
} }
TrendChartDataJSON += "]";
} }
TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]";
} }
} }
} }

View File

@ -1,5 +1,6 @@
@page @page
@model SalesAnalyzer @model SalesAnalyzer
@using System.Globalization
@{ @{
ViewData["Title"] = ""; ViewData["Title"] = "";
} }
@ -19,14 +20,11 @@
<thead> <thead>
<tr> <tr>
@{ @{
string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); double totalCurrentValue = double.Parse(Model.PTData.Misc.TotalCurrentValue);
if (Model.totalCurrentValue > 100) string totalCurrentValueString = Model.PTData.Misc.TotalCurrentValue;
{
totalCurrentValueString = Math.Round(Model.totalCurrentValue, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
} }
} <th class="m-t-0 header-title text-left">Total Current Value: &nbsp; <text class="text-autocolor"> @totalCurrentValueString @Model.Summary.MainMarket </text> <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is based on the Total Current Value reported by Profit Trailer."></i></small></th>
<th class="m-t-0 header-title text-left">Total Account Value: &nbsp; <text class="text-autocolor"> @totalCurrentValueString @Model.Summary.MainMarket </text> <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is based on your sales history, entries on the Transactions page and any currently held positions."></i></small></th> <th class="text-right">Starting Value: &nbsp; <text class="text-autocolor"> @Model.MiscData.StartBalance &nbsp; @Model.Summary.MainMarket </text> <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is the starting value set in your PT account settings."></i></small></th>
<th class="text-right">Starting Value: &nbsp; <text class="text-autocolor"> @Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance &nbsp; @Model.Summary.MainMarket </text> <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is the starting value found in your settings file"></i></small></th>
</tr> </tr>
</thead> </thead>
</table> </table>
@ -34,78 +32,129 @@
</div> </div>
</div> </div>
<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>
<div class="balance-chart">
<svg style="height:350px;width:100%"></svg>
</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-2" style="height:320px;">
<h4 class="m-t-0 header-title">No Sales!</h4> <h4 class="m-t-0 header-title"><b>Cumulative Profits</b>
@if (!Model.CumulativeProfitChartDataJSON.Equals("")) {
<div class="cumulative-profit-chart">
<svg style="height:300px;width:100%"></svg>
</div>
} else {
<p>Unable to load graph, no sales data found.</p>
}
</div>
</div>
<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> <div class="col-md-6">
<div class="card-box px-2" style="height:320px;">
<h4 class="m-t-0 header-title"><b>Daily TCV</b>
@if (!Model.TCVChartDataJSON.Equals("")) {
<div class="TCV-chart">
<svg style="height:300px;width:100%"></svg>
</div>
} else {
<p>Unable to load graph, no sales data found.</p>
}
</div> </div>
</div> </div>
</div> </div>
} else {
<div class="row">
<div class="col-md-6">
<div class="card-box px-3" style="height:340px;">
@{
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 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">
<h4 class="m-t-0 header-title">Sales Analysis</h4>
@{ @{
double totalProfit = 0; int totalDays = Model.PTData.DailyPNL.Count;
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit); double startBalance = Model.MiscData.StartBalance;
double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2); double totalSales = Model.PTData.Stats.TotalSales;
double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2); double totalProfit = Model.PTData.Stats.TotalProfit;
double avgDailyGain = Model.DailyGains.Values.Average(dg => dg); double totalFundingFees = Model.PTData.Stats.FundingTotal;
double avgMonthlyGain = Model.MonthlyGains.Values.Average(dg => dg); double totalPercentGain = ((totalProfit + totalFundingFees) / startBalance) * 100;
string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; double totalProfitFiat = @Math.Round((totalProfit + totalFundingFees) * Model.PTData.Misc.FiatConversionRate, 0);
if (Model.PTData.TransactionData.Transactions.Count > 0) { double avgDailySales = @Math.Round(totalSales/totalDays, 1);
percentGainText = "<i class=\"fa fa-info-circle text-muted\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"You have added at least one manual transaction, so the total gain percentage cannot be calculated.\"></i>"; double avgDailyGain = totalPercentGain / totalDays;
}
}
var MonthlyAveragesResult = Model.MonthlyAverages(Model.PTData.MonthlyStats, Model.PTData.DailyPNL);
string startDate = MonthlyAveragesResult.startDate.ToString("d");
string endDate = MonthlyAveragesResult.endDate.ToString("d");
double totalMonths = Math.Round(MonthlyAveragesResult.totalMonths, 1);
double avgMonthlySales = @Math.Round(totalSales / totalMonths, 1);
double avgMonthlyProfit = totalProfit / totalMonths;
double avgMonthlyGain = totalPercentGain / totalMonths;
double avgMonthlyFunding = totalFundingFees / totalMonths;
}
<h4 class="m-t-0 header-title" style="display: inline;">Averages</h4>&nbsp;&nbsp;&nbsp;&nbsp;<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</th> <th class="text-right">AVG/Day</span></th>
<th class="text-right">AVG/Month</th> <th class="text-right">AVG/Month</span> <small><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top"
title="Months are based on the size of the calendar months. Weighted to compensate for partial months."></i></small></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<th>Sales</th> <th>Sales</th>
<td class="text-right">@Model.PTData.SellLog.Count</td> <td class="text-right">@totalSales</td>
<td class="text-right">@Math.Round((double)Model.PTData.SellLog.Count / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date).ToList().Count, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right">@avgDailySales</td>
<td class="text-right">@Math.Round((double)Model.PTData.SellLog.Count / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date.ToString("yyyy-MM")).ToList().Count, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right">@avgMonthlySales</td>
</tr> </tr>
<tr> <tr>
<th>Profit</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 / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date).ToList().Count, 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">@Math.Round(totalProfit / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date.ToString("yyyy-MM")).ToList().Count, 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>
</tr>
@if(Model.PropertiesData.IsLeverageExchange)
{
<tr>
<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 / 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>
</tr>
}
<tr>
<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 / 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>
</tr> </tr>
<tr> <tr>
<th>Profit USD</th> <th>Gain</th>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> <td class="text-right text-autocolor">@totalPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(totalProfitFiat / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date).ToList().Count, 8).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> <td class="text-right text-autocolor">@avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(totalProfitFiat / (double)Model.PTData.SellLog.GroupBy(d => d.SoldDate.Date.ToString("yyyy-MM")).ToList().Count, 8).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> <td class="text-right text-autocolor">@avgMonthlyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr>
<tr>
<th>% Gain</th>
<td class="text-right text-autocolor">@Html.Raw(percentGainText)</td>
<td class="text-right text-autocolor">@avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
<td class="text-right text-autocolor">@avgMonthlyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -115,46 +164,52 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="card-box"> <div class="card-box">
@{ @{
double currentTotalBalance = Model.totalCurrentValue; double estimatedBalance1Week = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 7.0), 8);
double estimatedBalance1Month = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 30.0), 8); double estimatedBalance1Month = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 30.0), 8);
double estimatedBalance3Months = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 90.0), 8); double estimatedBalance3Months = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 90.0), 8);
double estimatedBalance6Months = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 180.0), 8); double estimatedBalance6Months = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 180.0), 8);
double estimatedBalance1Year = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 365.0), 8); double estimatedBalance1Year = Math.Round(totalCurrentValue * Math.Pow((1 + (avgDailyGain / 100)), 365.0), 8);
} }
<h4 class="m-t-0 header-title">Balance Prediction <small><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The balance prediction is based on your daily average gain of @avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% and your current approximate balance of @currentTotalBalance.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))"></i></small></h4> <h4 class="m-t-0 header-title">TCV Prediction <small><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The balance prediction is based on your daily average gain of @avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% and your current TCV of @totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))"></i></small></h4>
<table class="table table-striped table-sm"> <table class="table table-striped table-sm">
<thead> <thead>
<tr> <tr>
<th class="text-right"></th> <th class="text-right"></th>
<th class="text-right">Est. Balance</th> <th class="text-right">@Model.PTData.Misc.Market</th>
<th class="text-right">Est. @Model.Summary.MainFiatCurrency Value</th> <th class="text-right">@Model.PTData.Properties.Currency</th>
<th class="text-right">Est. Gain</th> <th class="text-right">Gain</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<th>1 month</th> <th>1 Week</th>
<td class="text-right text-autocolor">@estimatedBalance1Week.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@Html.Raw(Math.Round(estimatedBalance1Week * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@Math.Round((estimatedBalance1Week - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr>
<tr>
<th>1 Month</th>
<td class="text-right text-autocolor">@estimatedBalance1Month.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor">@estimatedBalance1Month.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance1Month * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> <td class="text-right text-autocolor">@Html.Raw(Math.Round(estimatedBalance1Month * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@Math.Round((estimatedBalance1Month - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> <td class="text-right text-autocolor">@Math.Round((estimatedBalance1Month - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr> </tr>
<tr> <tr>
<th>3 months</th> <th>3 Months</th>
<td class="text-right text-autocolor">@estimatedBalance3Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor">@estimatedBalance3Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance3Months * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> <td class="text-right text-autocolor">@Html.Raw(Math.Round(estimatedBalance3Months * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@Math.Round((estimatedBalance3Months - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> <td class="text-right text-autocolor">@Math.Round((estimatedBalance3Months - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr> </tr>
<tr> <tr>
<th>6 months</th> <th>6 Months</th>
<td class="text-right text-autocolor">@estimatedBalance6Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor">@estimatedBalance6Months.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance6Months * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> <td class="text-right text-autocolor">@Html.Raw(Math.Round(estimatedBalance6Months * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@Math.Round((estimatedBalance6Months - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> <td class="text-right text-autocolor">@Math.Round((estimatedBalance6Months - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr> </tr>
<tr> <tr>
<th>1 year</th> <th>1 Year</th>
<td class="text-right text-autocolor">@estimatedBalance1Year.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor">@estimatedBalance1Year.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + Math.Round(estimatedBalance1Year * Model.Summary.MainMarketPrice, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> <td class="text-right text-autocolor">@Html.Raw(Math.Round(estimatedBalance1Year * Model.MiscData.FiatConversionRate, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@Math.Round((estimatedBalance1Year - currentTotalBalance) / currentTotalBalance * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> <td class="text-right text-autocolor">@Math.Round((estimatedBalance1Year - totalCurrentValue) / totalCurrentValue * 100, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -162,54 +217,48 @@
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-6">
<div class="card-box">
<div class="trades-chart">
<svg style="height:220px;width:100%"></svg>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card-box">
<div class="profit-chart">
<svg style="height:220px;width:100%"></svg>
</div>
</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">
<h4 class="m-t-0 header-title">Last @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries days</h4> @{
var maxDays = Math.Min(Model.DailyStats.Count, Math.Min(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries, 30));
}
<h4 class="m-t-0 header-title">Last @maxDays Days</h4>
<table class="table table-sm"> <table class="table table-sm">
<thead> <thead>
<tr> <tr>
<th>Day</th> <th>Day</th>
<th class="text-right">Buys</th>
<th class="text-right">Sales</th> <th class="text-right">Sales</th>
<th class="text-right">Profit @Model.Summary.MainMarket</th> <th class="text-right">Profit @Model.Summary.MainMarket</th>
<th class="text-right">Profit @Model.Summary.MainFiatCurrency</th> <th class="text-right">Profit @Model.PTData.Properties.Currency</th>
<th class="text-right">% Gain</th> <th class="text-right">Gain</th>
</tr> </tr>
</thead> </thead>
<tbody> <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); for (int i = 0; i < maxDays; i++)
double salesDateProfit = 0; {
salesDateProfit = salesDateSales.Sum(sl => sl.Profit); DateTime salesDate = DateTime.ParseExact(Model.PTData.DailyStats[i].Date, "d-M-yyyy", CultureInfo.InvariantCulture);
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2); var buys = Model.PTData.DailyStats[i].TotalBuys;
double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesDate); var sales = Model.PTData.DailyStats[i].TotalSales;
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2); 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> <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>@salesDate.ToString("yyyy-MM-dd")</td>
<td class="text-right">@salesDateSales.Count</td> <td class="text-right text-autocolor"">@buys</td>
<td class="text-right text-autocolor">@salesDateProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor"">@sales</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">@profit.ToString("#,#0.00000000", 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">@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> </tr>
} }
}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -217,110 +266,83 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 header-title">Last @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries months</h4> @{
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"> <table class="table table-sm">
<thead> <thead>
<tr> <tr>
<th>Month</th> <th>Month</th>
<th class="text-right">Sales</th> <th class="text-right">Sales</th>
<th class="text-right">Profit @Model.Summary.MainMarket</th> <th class="text-right">Profit @Model.Summary.MainMarket</th>
<th class="text-right">Profit @Model.Summary.MainFiatCurrency</th> <th class="text-right">Profit @Model.PTData.Properties.Currency</th>
<th class="text-right">% Gain</th> <th class="text-right">Growth</th>
<th class="text-right">AVG %/Day</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@{ @{
DateTime minSellLogMonthDate = new DateTime(Model.MinSellLogDate.Year, Model.MinSellLogDate.Month, 1).Date; for (int i = 0; i < maxMonths; i++)
DateTime salesMonthStartDate = new DateTime(Model.DateTimeNow.DateTime.Year, Model.DateTimeNow.DateTime.Month, 1).Date; {
} DateTime monthDate = DateTime.ParseExact(Model.PTData.MonthlyStats[i].Month, "M-yyyy", CultureInfo.InvariantCulture);
@for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= Model.DateTimeNow.DateTime.AddMonths(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries) && salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) { string monthName = monthDate.ToString("MMMM", CultureInfo.InvariantCulture);
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesMonthSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year); var sales = Model.PTData.MonthlyStats[i].TotalSales;
double salesDateProfit = 0; var profit = Model.PTData.MonthlyStats[i].TotalProfitCurrency;
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit); var profitFiat = Math.Round(profit * Model.PTData.Misc.FiatConversionRate, 0);
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2); var growth = Math.Round(Model.PTData.MonthlyStats[i].AvgGrowth,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> <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>@monthName</td>
<td class="text-right text-autocolor">@salesMonthSales.Count</td> <td class="text-right text-autocolor">@sales</td>
<td class="text-right text-autocolor">@salesDateProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor">@profit.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">@profitFiat</td>
<td class="text-right text-autocolor">@salesDateGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> <td class="text-right text-autocolor">@growth %</td>
<td class="text-right text-autocolor">@salesDateAVGDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
</tr> </tr>
} }
}
</tbody> </tbody>
</table> </table>
</div> </div
</div>
</div> </div>
<div class="row"> @* <div class="col-md-6"> *@
<div class="col-sm-12">
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 header-title"><b>Top @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets Sales Market Analysis</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>
<thead> <thead>
<tr> <tr>
<th scope="col" data-tablesaw-priority="persist" data-tablesaw-sortable-col data-tablesaw-sortable-default-col>Rank</th> <th scope="col" data-tablesaw-priority="persist" data-tablesaw-sortable-col data-tablesaw-sortable-default-col>Rank</th>
<th scope="col" data-tablesaw-sortable-col>Market</th> <th scope="col" data-tablesaw-sortable-col>Market</th>
<th scope="col" class="text-right" data-tablesaw-sortable-col>Profit @Model.PTData.Misc.Market</th>
<th scope="col" class="text-right" data-tablesaw-sortable-col>Sales</th> <th scope="col" class="text-right" data-tablesaw-sortable-col>Sales</th>
<th scope="col" class="text-right" data-tablesaw-sortable-col>Profit @Model.Summary.MainMarket</th> <th scope="col" class="text-right" data-tablesaw-sortable-col>Avg/Trade</th>
<th scope="col" class="text-right" data-tablesaw-sortable-col>Profit @Model.Summary.MainFiatCurrency</th>
<th scope="col" class="text-right" data-tablesaw-sortable-col>Profit @Model.Summary.MainFiatCurrency/Trade</th>
<th scope="col" class="text-right" data-tablesaw-sortable-col>Profit %/Trade</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@{ @{
var topMarkets = Model.PTData.SellLog.GroupBy(m => m.Market).Select(mg => mg.Sum(m => m.Profit)); int rank = 1;
int marketRank = 0; foreach (var pair in Model.ProfitablePairs)
} {
@foreach (KeyValuePair<string, double> marketData in Model.TopMarkets) { string coin = pair.Coin;
marketRank++; double profit = Math.Round(pair.ProfitCurrency,8);
int trades = Model.PTData.SellLog.FindAll(m => m.Market == marketData.Key).Count; int sales = pair.SoldTimes;
double profitFiat = Math.Round(marketData.Value * Model.Summary.MainMarketPrice, 2); double avg = Math.Round(pair.Avg,8);
double profitFiatPerTrade = Math.Round(profitFiat / trades, 2); double profitFiat = Math.Round(profit * Model.MiscData.FiatConversionRate, 0);
<tr> <tr>
<td>@marketRank</td> <td>@rank</td>
<td><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, marketData.Key, Model.Summary.MainMarket)" target="_blank">@marketData.Key</a></td> <td><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, coin, Model.PTData.Misc.Market)" target="_blank">@coin</a></td>
<td class="text-right">@trades</td> <td class="text-right text-autocolor-saw">@profit</td>
<td class="text-right text-autocolor-saw">@marketData.Value.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right">@sales</td>
<td class="text-right text-autocolor-saw">@profitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) @Model.Summary.MainFiatCurrency</td> <td class="text-right text-autocolor-saw">@avg </td>
<td class="text-right text-autocolor-saw">@profitFiatPerTrade.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) @Model.Summary.MainFiatCurrency</td>
<td class="text-right text-autocolor-saw">@Model.PTData.SellLog.FindAll(m => m.Market == marketData.Key).Average(p => p.ProfitPercent).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
</tr> </tr>
rank++;
}
} }
</tbody> </tbody>
</table> </table>
</div> </div>
</div> @* </div> *@
</div>
<div id="salesList" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" style="display: none;"> </div>
<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 {
@ -343,60 +365,198 @@
<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';
nv.addGraph(function () { $('[role="tooltip"]').remove();
var lineChart = nv.models.lineChart(); $('[data-toggle="tooltip"]').tooltip();
var height = 300;
/**/
var chartData = @Html.Raw(Model.TradesChartDataJSON);
/**/
lineChart.useInteractiveGuideline(true);
lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); });
lineChart.yAxis.axisLabel('Daily Sales').tickFormat(d3.format(','));
d3.select('.trades-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
nv.utils.windowResize(lineChart.update);
return lineChart;
});
nv.addGraph(function () {
var lineChart = nv.models.lineChart();
var height = 300;
/**/
var chartData = @Html.Raw(Model.ProfitChartDataJSON);
/**/
lineChart.useInteractiveGuideline(true);
lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); });
lineChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f'));
d3.select('.profit-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
nv.utils.windowResize(lineChart.update);
return lineChart;
});
nv.addGraph(function () {
var lineChart = nv.models.lineChart();
var height = 400;
/**/
var chartData = @Html.Raw(Model.BalanceChartDataJSON);
/**/
lineChart.useInteractiveGuideline(true);
lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); });
lineChart.yAxis.axisLabel('Profit').tickFormat(d3.format(',.2f'));
d3.select('.balance-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
nv.utils.windowResize(lineChart.update);
return lineChart;
});
$("#salesList").on("show.bs.modal", function (e) {
$(this).find(".modal-content").html('<i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i>');
var link = $(e.relatedTarget);
$(this).find(".modal-content").load(link.attr("href"), function () {
$('.text-autocolor').autocolor(false); $('.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 () {
salesChart = nv.models.multiBarChart();
var height = 300;
salesChart.groupSpacing(0.5);
salesChart.showControls(false);
//salesChart.useInteractiveGuideline(true);
salesChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); });
salesChart.yAxis.axisLabel('').tickFormat(d3.format(',.2f'));
salesData = @Html.Raw(Model.SalesChartDataJSON);
d3.select('.sales-chart svg')
.datum(salesData)
.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);
}); });
d3.select('body').on('mousemove', function() {
var chartBounds = d3.select('.sales-chart')[0][0].getBoundingClientRect();
var mouseX = d3.event.clientX;
var mouseY = d3.event.clientY;
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
d3.select('.nvtooltip').style('opacity', 0);
}
}); });
nv.utils.windowResize(salesChart.update);
return salesChart;
});
</text>
}
})(jQuery); })(jQuery);
</script>
<script type="text/javascript">
(function ($) {
'use strict';
$('[role="tooltip"]').remove();
$('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false);
var cumulativeProfitChart; // Keep a reference to the chart
var cumulativeProfitData; // Keep a reference to the data
@if (!Model.CumulativeProfitChartDataJSON.Equals("")) {
<text>
nv.addGraph(function () {
cumulativeProfitChart = nv.models.lineChart();
var height = 300;
cumulativeProfitChart.useInteractiveGuideline(true);
cumulativeProfitChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); });
cumulativeProfitChart.yAxis.axisLabel('').tickFormat(d3.format(',.2f'));
cumulativeProfitData = @Html.Raw(Model.CumulativeProfitChartDataJSON);
d3.select('.cumulative-profit-chart svg')
.datum(cumulativeProfitData)
.transition().duration(0)
.call(cumulativeProfitChart);
// Add mouseleave, and mousemove event listeners to hide tooltip
d3.select('.cumulative-profit-chart').on('mouseleave', function() {
d3.select('.nvtooltip').style('opacity', 0);
});
d3.select('body').on('mousemove', function() {
var chartBounds = d3.select('.cumulative-profit-chart')[0][0].getBoundingClientRect();
var mouseX = d3.event.clientX;
var mouseY = d3.event.clientY;
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
d3.select('.nvtooltip').style('opacity', 0);
}
});
nv.utils.windowResize(cumulativeProfitChart.update);
return cumulativeProfitChart;
});
</text>
}
})(jQuery);
</script> </script>
<script type="text/javascript">
(function ($) {
'use strict';
$('[role="tooltip"]').remove();
$('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false);
var TCVChart; // Keep a reference to the chart
var TCVData; // Keep a reference to the data
@if (!Model.TCVChartDataJSON.Equals("")) {
<text>
nv.addGraph(function () {
TCVChart = nv.models.lineChart();
var height = 300;
TCVChart.useInteractiveGuideline(true);
TCVChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); });
TCVChart.yAxis.axisLabel('').tickFormat(d3.format(',.2f'));
TCVData = @Html.Raw(Model.TCVChartDataJSON);
d3.select('.TCV-chart svg')
.datum(TCVData)
.transition().duration(0)
.call(TCVChart);
// Add mouseleave, and mousemove event listeners to hide tooltip
d3.select('.TCV-chart').on('mouseleave', function() {
d3.select('.nvtooltip').style('opacity', 0);
});
d3.select('body').on('mousemove', function() {
var chartBounds = d3.select('.TCV-chart')[0][0].getBoundingClientRect();
var mouseX = d3.event.clientX;
var mouseY = d3.event.clientY;
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
d3.select('.nvtooltip').style('opacity', 0);
}
});
nv.utils.windowResize(TCVChart.update);
return TCVChart;
});
</text>
}
})(jQuery);
</script>
<script type="text/javascript">
(function ($) {
'use strict';
$('[role="tooltip"]').remove();
$('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false);
var profitChart; // Keep a reference to the chart
var profitData; // Keep a reference to the data
@if (!Model.ProfitChartDataJSON.Equals("")) {
<text>
nv.addGraph(function () {
profitChart = nv.models.lineChart();
var height = 300;
profitChart.useInteractiveGuideline(true);
profitChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); });
profitChart.yAxis.axisLabel('').tickFormat(d3.format(',.2f'));
profitData = @Html.Raw(Model.ProfitChartDataJSON);
d3.select('.profit-chart svg')
.datum(profitData)
.transition().duration(0)
.call(profitChart);
// Add mouseleave, and mousemove event listeners to hide tooltip
d3.select('.profit-chart').on('mouseleave', function() {
d3.select('.nvtooltip').style('opacity', 0);
});
d3.select('body').on('mousemove', function() {
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
var mouseX = d3.event.clientX;
var mouseY = d3.event.clientY;
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
d3.select('.nvtooltip').style('opacity', 0);
}
});
nv.utils.windowResize(profitChart.update);
return profitChart;
});
</text>
}
})(jQuery);
</script>
} }

View File

@ -1,146 +1,352 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Core.Main; using Core.Main;
using Core.Helper; 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
{ {
public ProfitTrailerData PTData = null; public ProfitTrailerData PTData = null;
public MiscData MiscData { get; set; }
public PropertiesData PropertiesData { get; set; }
public StatsData StatsData { get; set; }
public List<DailyPNLData> DailyPNL { get; set; }
public List<DailyTCVData> DailyTCV { get; set; }
public List<ProfitablePairsData> ProfitablePairs { get; set; }
public List<DailyStatsData> DailyStats { get; set; }
public int ProfitDays { get; set; }
public int TCVDays { get; set; }
public int SalesDays { get; set; }
public List<MonthlyStatsData> MonthlyStats { get; set; }
public string TradesChartDataJSON = ""; public string TradesChartDataJSON = "";
public string CumulativeProfitChartDataJSON = "";
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()
{ {
base.Init(); base.Init();
BindData(); BindData();
BuildTCV();
} }
private void BindData() private void BindData()
{ {
PTData = this.PtDataObject; PTData = this.PtDataObject;
MiscData = this.PTData.Misc;
PropertiesData = this.PTData.Properties;
StatsData = this.PTData.Stats;
MonthlyStats = this.PTData.MonthlyStats;
DailyPNL = this.PTData.DailyPNL;
DailyTCV = this.PTData.DailyTCV;
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("+", ""));
DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan); DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan);
BuildTopMarkets();
BuildSalesChartData(); BuildSalesChartData();
BuildProfitChartData();
BuildCumulativeProfitChartData();
BuildTCVChartData();
} }
private void BuildTopMarkets()
private void BuildTCVChartData()
{ {
var markets = PTData.SellLog.GroupBy(m => m.Market); List<object> TCVPerDayList = new List<object>();
Dictionary<string, double> topMarketsDic = new Dictionary<string, double>();
foreach (var market in markets) if (PTData.DailyTCV.Count > 0)
{ {
double totalProfit = 0; // Get timezone offset
totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit); TimeSpan offset;
topMarketsDic.Add(market.Key, totalProfit); bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
{
offset = TimeSpan.Zero; // If offset is invalid, set it to zero
} }
TopMarkets = new SortedDictionary<string, double>(topMarketsDic).OrderByDescending(m => m.Value).Take(PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets);
DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date;
// Parse dates once and adjust them to the local timezone
Dictionary<DateTime, DailyTCVData> dailyTCVByDate = PTData.DailyTCV
.Select(data => {
DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture);
DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset);
return new { Date = dateLocal.Date, Data = data };
})
.ToDictionary(
item => item.Date,
item => item.Data
);
DateTime earliestDataDate = dailyTCVByDate.Keys.Min();
DateTime startDate = earliestDataDate;
// Calculate the total days of data available
TCVDays = (endDate - startDate).Days;
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{
// Use the dictionary to find the Data for the date
if (dailyTCVByDate.TryGetValue(date, out DailyTCVData dailyTCV))
{
double TCV = dailyTCV.TCV;
// Add the data point to the list
TCVPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = TCV });
} }
}
// Convert the list to a JSON string using Newtonsoft.Json
TCVChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] {
new {
key = "TCV in " + PTData.Misc.Market,
color = Constants.ChartLineColors[1],
values = TCVPerDayList
}
});
}
}
private void BuildCumulativeProfitChartData()
{
List<object> profitPerDayList = new List<object>();
if (PTData.DailyPNL.Count > 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))
{
offset = TimeSpan.Zero; // If offset is invalid, set it to zero
}
DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date;
// Parse dates once and adjust them to the local timezone
Dictionary<DateTime, DailyPNLData> dailyPNLByDate = PTData.DailyPNL
.Select(data => {
DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture);
DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset);
return new { Date = dateLocal.Date, Data = data };
})
.ToDictionary(
item => item.Date,
item => item.Data
);
DateTime earliestDataDate = dailyPNLByDate.Keys.Min();
DateTime startDate = earliestDataDate;
// Calculate the total days of data available
ProfitDays = (endDate - startDate).Days;
double previousDayCumulativeProfit = 0;
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{
// Use the dictionary to find the DailyPNLData for the date
if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL))
{
// Use the CumulativeProfitCurrency directly
double profitFiat = dailyPNL.CumulativeProfitCurrency;
// Add the data point to the list
profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat });
previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency;
}
}
// Convert the list to a JSON string using Newtonsoft.Json
CumulativeProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] {
new {
key = "Profit in " + PTData.Misc.Market,
color = Constants.ChartLineColors[1],
values = profitPerDayList
}
});
}
}
private void BuildProfitChartData()
{
List<object> profitPerDayList = new List<object>();
if (PTData.DailyPNL.Count > 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))
{
offset = TimeSpan.Zero; // If offset is invalid, set it to zero
}
DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date;
// Parse dates once and adjust them to the local timezone
Dictionary<DateTime, DailyPNLData> dailyPNLByDate = PTData.DailyPNL
.Select(data => {
DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture);
DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset);
return new { Date = dateLocal.Date, Data = data };
})
.ToDictionary(
item => item.Date,
item => item.Data
);
DateTime earliestDataDate = dailyPNLByDate.Keys.Min();
DateTime startDate = earliestDataDate;
// Calculate the total days of data available
ProfitDays = (endDate - startDate).Days;
double previousDayCumulativeProfit = 0;
bool isFirstDay = true;
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{
// Use the dictionary to find the DailyPNLData for the date
if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL))
{
if (isFirstDay)
{
isFirstDay = false;
}
else
{
// Calculate the profit for the current day
double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2);
// Add the data point to the list
profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat });
}
previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency;
}
}
// Convert the list to a JSON string using Newtonsoft.Json
ProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] {
new {
key = "Profit in " + PTData.Misc.Market,
color = Constants.ChartLineColors[1],
values = profitPerDayList
}
});
}
}
public (double totalMonths, DateTime startDate, DateTime endDate) MonthlyAverages(List<MonthlyStatsData> monthlyStats, List<DailyPNLData> dailyPNL)
{
double totalMonths = 0;
// Get the exact start and end dates of sales data
DateTime startDate = dailyPNL.Min(d => DateTime.ParseExact(d.Date, "d-M-yyyy", CultureInfo.InvariantCulture));
DateTime endDate = dailyPNL.Max(d => DateTime.ParseExact(d.Date, "d-M-yyyy", CultureInfo.InvariantCulture));
int daysInFirstMonth = DateTime.DaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1;
int daysInLastMonth = endDate.Day;
//Console.WriteLine("Start Date: {0}, End Date: {1}, Days in first month: {2}, Days in last month: {3}", startDate, endDate, daysInFirstMonth, daysInLastMonth);
for (int i = 0; i < monthlyStats.Count; i++)
{
var monthStat = monthlyStats[i];
double weight;
// Parse the Month property into a DateTime object
DateTime monthDate = DateTime.ParseExact(monthStat.Month, "M-yyyy", CultureInfo.InvariantCulture);
// If it's the first or last month in the dataset, calculate the weight based on the number of days
if (i == 0)
{
// Calculate weight based on the number of days in the dataset for the first month
weight = daysInFirstMonth / 30.0;
}
else if (i == monthlyStats.Count - 1)
{
// Calculate weight based on the number of days in the dataset for the last month
weight = (daysInLastMonth / 30.0);
}
else
{
// Otherwise, assume it's a full month
weight = 1;
}
totalMonths += weight;
}
return (totalMonths, startDate, endDate);
}
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 = "";
string profitPerDayJSON = "";
string balancePerDayJSON = "";
double balance = 0.0;
for (DateTime salesDate = graphStartDate; salesDate <= DateTimeNow.DateTime.Date; salesDate = salesDate.AddDays(1))
{ {
if (tradeDayIndex > 0) // Get timezone offset
{ TimeSpan offset;
tradesPerDayJSON += ",\n"; bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
profitPerDayJSON += ",\n"; string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
balancePerDayJSON += ",\n";
}
double profit = 0;
int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Count;
profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit);
double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2);
balance += profitFiat;
tradesPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + trades + "}";
profitPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
balancePerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + balance.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
tradeDayIndex++;
}
TradesChartDataJSON = "[";
TradesChartDataJSON += "{";
TradesChartDataJSON += "key: 'Sales',";
TradesChartDataJSON += "color: '" + Constants.ChartLineColors[0] + "',";
TradesChartDataJSON += "values: [" + tradesPerDayJSON + "]";
TradesChartDataJSON += "}";
TradesChartDataJSON += "]";
ProfitChartDataJSON = "["; if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
ProfitChartDataJSON += "{"; {
ProfitChartDataJSON += "key: 'Profit in " + Summary.MainFiatCurrency + "',"; offset = TimeSpan.Zero; // If offset is invalid, set it to zero
ProfitChartDataJSON += "color: '" + Constants.ChartLineColors[1] + "',"; }
ProfitChartDataJSON += "values: [" + profitPerDayJSON + "]";
ProfitChartDataJSON += "}";
ProfitChartDataJSON += "]";
BalanceChartDataJSON = "["; DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date;
BalanceChartDataJSON += "{";
BalanceChartDataJSON += "key: 'Profit in " + Summary.MainFiatCurrency + "',";
BalanceChartDataJSON += "color: '" + Constants.ChartLineColors[1] + "',";
BalanceChartDataJSON += "values: [" + balancePerDayJSON + "]";
BalanceChartDataJSON += "}";
BalanceChartDataJSON += "]";
for (DateTime salesDate = DateTimeNow.DateTime.Date; salesDate >= MinSellLogDate; salesDate = salesDate.AddDays(-1)) // 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
{ {
List<SellLogData> salesDateSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate); if (salesCountByDate.TryGetValue(date, out DailyStatsData dailyStatsData))
double salesDateProfit;
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
double salesDateStartBalance = PTData.GetSnapshotBalance(salesDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
DailyGains.Add(salesDate, salesDateGain);
}
DateTime minSellLogMonthDate = new DateTime(MinSellLogDate.Year, MinSellLogDate.Month, 1).Date;
DateTime salesMonthStartDate = new DateTime(DateTimeNow.DateTime.Year, DateTimeNow.DateTime.Month, 1).Date;
for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1))
{ {
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesMonthSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year); buysPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = Convert.ToInt32(dailyStatsData.TotalBuys) });
double salesDateProfit; salesPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = Convert.ToInt32(dailyStatsData.TotalSales) });
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
double salesDateStartBalance = PTData.GetSnapshotBalance(salesMonthDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
MonthlyGains.Add(salesMonthDate, salesDateGain);
} }
} }
// Convert the lists to a JSON string using Newtonsoft.Json
SalesChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] {
new { // New JSON object for daily buys
key = "Buys",
color = Constants.ChartLineColors[19], // Use a different color for buys
values = buysPerDayList
},
new {
key = "Sales",
color = Constants.ChartLineColors[1],
values = salesPerDayList
} }
private void BuildTCV() });
{
double AvailableBalance = PTData.GetCurrentBalance();
foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog)
{
double leverage = dcaLogEntry.Leverage;
if (leverage == 0)
{
leverage = 1;
} }
totalCurrentValue = totalCurrentValue + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) / leverage);
}
totalCurrentValue = totalCurrentValue + AvailableBalance;
} }
} }
} }

View File

@ -124,19 +124,19 @@
</div> </div>
</div> </div>
<div class="form-group row"> @* <div class="form-group row">
<label class="col-md-4 col-form-label">Main Fiat Currency <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is the local currency you want PTM to use when showing your sales and account value in fiat."></i></label> <label class="col-md-4 col-form-label">Main Fiat Currency <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is the local currency you want PTM to use when showing your sales and account value in fiat."></i></label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" class="form-control" name="Application_MainFiatCurrency" value="@Model.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency"> <input type="text" class="form-control" name="Application_MainFiatCurrency" value="@Model.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency">
</div> </div>
</div> </div> *@
<div class="form-group row"> @* <div class="form-group row">
<label class="col-md-4 col-form-label">Starting Balance <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="PTM will use this to calculate total profits to date, and projected profits."></i></label> <label class="col-md-4 col-form-label">Starting Balance <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="PTM will use this to calculate total profits to date, and projected profits."></i></label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" class="form-control" name="Application_StartBalance" value="@Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance"> <input type="text" class="form-control" name="Application_StartBalance" value="@Model.SummaryData.StartBalance">
</div>
</div> </div>
</div> *@
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">CoinMarketCap API Key <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The API Key that will be used to get Coin Data from CoinMarketCap (optional)."></i></label> <label class="col-md-4 col-form-label">CoinMarketCap API Key <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The API Key that will be used to get Coin Data from CoinMarketCap (optional)."></i></label>
@ -145,12 +145,12 @@
</div> </div>
</div> </div>
<div class="form-group row"> @* <div class="form-group row">
<label class="col-md-4 col-form-label">FreeCurrencyConverter API Key <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The api key needed for currency conversions for non-USD currency users (optional)."></i></label> <label class="col-md-4 col-form-label">FreeCurrencyConverter API Key <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The api key needed for currency conversions for non-USD currency users (optional)."></i></label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" class="form-control" name="Application_FreeCurrencyConverterAPIKey" value="@Model.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey"> <input type="text" class="form-control" name="Application_FreeCurrencyConverterAPIKey" value="@Model.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey">
</div> </div>
</div> </div> *@
</div> </div>
</div> </div>
@ -196,21 +196,28 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">Graph Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The interval for the monitor market trend graph to draw points."></i></label> <label class="col-md-4 col-form-label">Market Trend Graph Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The time interval for the market trend graph (Dashboard and Sales Analyzer) between data points. Very small intervals on large timeframe graphs can significantly impact performance."></i></label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphIntervalMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes.ToString(new System.Globalization.CultureInfo("en-US"))"> <input type="text" class="form-control" name="Monitor_GraphIntervalMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes.ToString(new System.Globalization.CultureInfo("en-US"))">
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">Graph Max Timeframe Hours <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the timeframe that your graph for market trends covers."></i></label> <label class="col-md-4 col-form-label">Market Trend Graph Max Timeframe <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the market trends graph (Dashboard and Sales Analyzer) in hours. Large timeframe graphs can significantly impact performance."></i></label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphMaxTimeframeHours" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours.ToString(new System.Globalization.CultureInfo("en-US"))"> <input type="text" class="form-control" name="Monitor_GraphMaxTimeframeHours" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours.ToString(new System.Globalization.CultureInfo("en-US"))">
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">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">Daily Profit Graph Max Timeframe <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the daily profits graph on the dashboard in days. Large timeframes can significantly impact performance."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_ProfitsMaxTimeframeDays" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<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>
@ -240,6 +247,13 @@
</div> </div>
</div> </div>
@* <div class="form-group row">
<label class="col-md-4 col-form-label">Max Sales Records<i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The number of sales records PTMagic pulls from Profit Trailer. Changes require a Monitor Restart."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxSalesRecords" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div> *@
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">Max Top Markets <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of top markets being show in your Sales Analyzer."></i></label> <label class="col-md-4 col-form-label">Max Top Markets <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of top markets being show in your Sales Analyzer."></i></label>
<div class="col-md-8"> <div class="col-md-8">
@ -262,7 +276,7 @@
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-md-4 col-form-label">Max Dashboard Buy Entries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of entries being shown in your dashboard for possible buys."></i></label> <label class="col-md-4 col-form-label">Max Dashboard Buy Entries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of possible buys shown in your dashboard. Setting this to 0 completely hides the PBL table."></i></label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDashboardBuyEntries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries.ToString(new System.Globalization.CultureInfo("en-US"))"> <input type="text" class="form-control" name="Monitor_MaxDashboardBuyEntries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div> </div>

View File

@ -1,16 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Core.Main; using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
namespace Monitor.Pages namespace Monitor.Pages
{ {
public class SettingsGeneralModel : _Internal.BasePageModelSecure public class SettingsGeneralModel : _Internal.BasePageModelSecure
{ {
public string ValidationMessage = ""; public string ValidationMessage = "";
public ProfitTrailerData PTData = null;
public MiscData MiscData { get; set; }
private string GetTimezoneOffsetString(TimeZoneInfo tzi) private string GetTimezoneOffsetString(TimeZoneInfo tzi)
{ {
@ -51,6 +54,8 @@ namespace Monitor.Pages
public void OnGet() public void OnGet()
{ {
base.Init(); base.Init();
PTData = this.PtDataObject;
MiscData = this.PTData.Misc;
string notification = GetStringParameter("n", ""); string notification = GetStringParameter("n", "");
if (notification.Equals("BackupRestored")) if (notification.Equals("BackupRestored"))
@ -68,27 +73,29 @@ namespace Monitor.Pages
// Read the new settings // Read the new settings
PTMagicConfiguration.GeneralSettings.Application.IsEnabled = HttpContext.Request.Form["Application_IsEnabled"].Equals("on"); PTMagicConfiguration.GeneralSettings.Application.IsEnabled = HttpContext.Request.Form["Application_IsEnabled"].Equals("on");
PTMagicConfiguration.GeneralSettings.Application.TestMode = HttpContext.Request.Form["Application_TestMode"].Equals("on"); PTMagicConfiguration.GeneralSettings.Application.TestMode = HttpContext.Request.Form["Application_TestMode"].Equals("on");
PTMagicConfiguration.GeneralSettings.Application.StartBalance = SystemHelper.TextToDouble(HttpContext.Request.Form["Application_StartBalance"], PTMagicConfiguration.GeneralSettings.Application.StartBalance, "en-US"); //PTMagicConfiguration.GeneralSettings.Application.StartBalance = SystemHelper.TextToDouble(HttpContext.Request.Form["Application_StartBalance"], PTMagicConfiguration.GeneralSettings.Application.StartBalance, "en-US");
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName = HttpContext.Request.Form["Application_ProfitTrailerDefaultSettingName"]; PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName = HttpContext.Request.Form["Application_ProfitTrailerDefaultSettingName"];
PTMagicConfiguration.GeneralSettings.Application.Exchange = HttpContext.Request.Form["Application_Exchange"]; PTMagicConfiguration.GeneralSettings.Application.Exchange = HttpContext.Request.Form["Application_Exchange"];
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL = HttpContext.Request.Form["Application_ProfitTrailerMonitorURL"]; PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL = HttpContext.Request.Form["Application_ProfitTrailerMonitorURL"];
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken = HttpContext.Request.Form["Application_ProfitTrailerServerAPIToken"]; PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken = HttpContext.Request.Form["Application_ProfitTrailerServerAPIToken"];
PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset = HttpContext.Request.Form["Application_TimezoneOffset"]; PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset = HttpContext.Request.Form["Application_TimezoneOffset"];
PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency = HttpContext.Request.Form["Application_MainFiatCurrency"]; //PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency = HttpContext.Request.Form["Application_MainFiatCurrency"];
PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Application_FloodProtectionMinutes"], PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes); PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Application_FloodProtectionMinutes"], PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes);
PTMagicConfiguration.GeneralSettings.Application.InstanceName = HttpContext.Request.Form["Application_InstanceName"]; PTMagicConfiguration.GeneralSettings.Application.InstanceName = HttpContext.Request.Form["Application_InstanceName"];
PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey = HttpContext.Request.Form["Application_CoinMarketCapAPIKey"]; PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey = HttpContext.Request.Form["Application_CoinMarketCapAPIKey"];
PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"]; //PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"];
PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = HttpContext.Request.Form["Monitor_IsPasswordProtected"].Equals("on"); PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = HttpContext.Request.Form["Monitor_IsPasswordProtected"].Equals("on");
PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart = HttpContext.Request.Form["Monitor_OpenBrowserOnStart"].Equals("on"); PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart = HttpContext.Request.Form["Monitor_OpenBrowserOnStart"].Equals("on");
PTMagicConfiguration.GeneralSettings.Monitor.AnalyzerChart = HttpContext.Request.Form["Monitor_AnalyzerChart"]; PTMagicConfiguration.GeneralSettings.Monitor.AnalyzerChart = HttpContext.Request.Form["Monitor_AnalyzerChart"];
PTMagicConfiguration.GeneralSettings.Monitor.Port = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_Port"], PTMagicConfiguration.GeneralSettings.Monitor.Port); 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.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.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.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.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); PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BuyAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds);
PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform = HttpContext.Request.Form["Monitor_LinkPlatform"]; PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform = HttpContext.Request.Form["Monitor_LinkPlatform"];
//PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxSalesRecords"], PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords);
PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxTopMarkets"], PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets); PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxTopMarkets"], PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets);
PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxDailySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries); PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxDailySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries);
PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxMonthlySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries); PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxMonthlySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries);

View File

@ -10,7 +10,8 @@
} }
<div class="row"> <div class="row">
<div class="col-md-5">
<div class="col-md-6">
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 header-title">PTMagic Status <small id="last-refresh" class="pull-right"></small></h4> <h4 class="m-t-0 header-title">PTMagic Status <small id="last-refresh" class="pull-right"></small></h4>
@{ @{
@ -67,11 +68,10 @@
</div> </div>
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 header-title">Active Settings</h4> <h4 class="m-t-0 header-title">Active Settings &nbsp; <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If DYNAMIC formulas are used in PT for these settings, the values will not be properly displayed here."></i></h4>
@{ @{
string maxCostCaption = "Initial"; string maxCostCaption = "Initial";
} }
<table class="table table-striped table-sm"> <table class="table table-striped table-sm">
<tbody> <tbody>
<tr> <tr>
@ -86,8 +86,6 @@
} }
</tr> </tr>
<tr> <tr>
<td>DCA Level</td>
<td class="text-right">@Model.Summary.DCALevels.ToString(new System.Globalization.CultureInfo("en-US"))</td>
<td>Min Vol.</td> <td>Min Vol.</td>
<td class="text-right">@Model.Summary.MinBuyVolume.ToString(new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right">@Model.Summary.MinBuyVolume.ToString(new System.Globalization.CultureInfo("en-US"))</td>
</tr> </tr>
@ -101,90 +99,32 @@
</table> </table>
</div> </div>
<div class="card-box">
<h4 class="m-t-0 header-title">Active Buy Strategies</h4>
<table class="table table-striped table-sm">
<tbody>
@if (Model.Summary.BuyStrategies.Count == 0) {
<tr>
<td>Buy Strat.</td>
<td class="text-right">@Model.Summary.BuyStrategy</td>
<td>Buy Value</td>
<td class="text-right">@Model.Summary.BuyValue.ToString(new System.Globalization.CultureInfo("en-US"))</td>
</tr>
} else {
char buyStrategyIndex = 'A';
foreach (Core.Main.DataObjects.PTMagicData.StrategySummary buyStrategy in Model.Summary.BuyStrategies) {
<tr>
<td>Buy Strat. @buyStrategyIndex</td>
<td class="text-right">@buyStrategy.Name</td>
<td>Buy Value @buyStrategyIndex</td>
<td class="text-right">@buyStrategy.Value.ToString(new System.Globalization.CultureInfo("en-US"))</td>
</tr>
buyStrategyIndex++;
}
}
</tbody>
</table>
</div> </div>
<div class="card-box">
<h4 class="m-t-0 header-title">Active Sell Strategies</h4>
<table class="table table-striped table-sm">
<tbody>
@if (Model.Summary.SellStrategies.Count == 0) {
<tr>
<td>Sell Strat.</td>
<td class="text-right">@Model.Summary.SellStrategy</td>
<td>Sell Value</td>
<td class="text-right">@Model.Summary.SellValue.ToString(new System.Globalization.CultureInfo("en-US"))</td>
</tr>
} else {
char sellStrategyIndex = 'A';
foreach (Core.Main.DataObjects.PTMagicData.StrategySummary sellStrategy in Model.Summary.SellStrategies) {
<tr>
<td>Sell Strat. @sellStrategyIndex</td>
<td class="text-right">@sellStrategy.Name</td>
<td>Sell Value @sellStrategyIndex</td>
<td class="text-right">@sellStrategy.Value.ToString(new System.Globalization.CultureInfo("en-US"))</td>
</tr>
sellStrategyIndex++;
}
}
</tbody>
</table>
</div>
</div>
<div class="col-md-7">
<div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 header-title">Settings Active Time (Last 24h)</h4> <h4 class="m-t-0 header-title">Settings Active Time (Last 24h)</h4>
<div id="gsChart24h"> <div id="gsChart24h">
<svg style="height:300px;width:100%"></svg> <svg style="height:200px;width:100%"></svg>
</div> </div>
</div> </div>
</div>
<div class="col-md-6">
<div class="card-box"> <div class="card-box">
<h4 class="m-t-0 header-title">Settings Active Time (Last 3 days)</h4> <h4 class="m-t-0 header-title">Settings Active Time (Last 3 days)</h4>
<div id="gsChart3d"> <div id="gsChart3d">
<svg style="height:300px;width:100%"></svg> <svg style="height:200px;width:100%"></svg>
</div> </div>
</div> </div>
</div>
</div> </div>
<div class="card-box">
</div>
<div class="card-box">
<h4 class="m-t-0 header-title">Global Settings Log</h4> <h4 class="m-t-0 header-title">Global Settings Log</h4>
<table class="table table-striped table-sm"> <table class="table table-striped table-sm">
<thead> <thead>
<tr> <tr>
@ -216,9 +156,6 @@
} }
</tbody> </tbody>
</table> </table>
</div>
</div>
</div> </div>

View File

@ -101,7 +101,6 @@
<ul class="submenu"> <ul class="submenu">
<li><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SettingsGeneral"><i class="fa fa-power-off"></i> General</a></li> <li><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SettingsGeneral"><i class="fa fa-power-off"></i> General</a></li>
<li><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SettingsAnalyzer"><i class="fa fa-magic"></i> Analyzer</a></li> <li><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SettingsAnalyzer"><i class="fa fa-magic"></i> Analyzer</a></li>
<li><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)Transactions"><i class="fa fa-exchange"></i> Transactions</a></li>
<li><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)PresetFiles"><i class="fa fa-edit"></i> Presets Files</a></li> <li><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)PresetFiles"><i class="fa fa-edit"></i> Presets Files</a></li>
</ul> </ul>
</li> </li>

View File

@ -82,9 +82,9 @@
double sellTriggerPrice = Model.DCALogData.AverageBuyPrice + (Model.DCALogData.AverageBuyPrice * Model.DCALogData.SellTrigger / 100); double sellTriggerPrice = Model.DCALogData.AverageBuyPrice + (Model.DCALogData.AverageBuyPrice * Model.DCALogData.SellTrigger / 100);
double averageProfitPercent = 0; double averageProfitPercent = 0;
if (Model.PTData.SellLog.FindAll(m => m.Market == Model.DCALogData.Market).Count > 0) { @* if (Model.PTData.SellLog.FindAll(m => m.Market == Model.DCALogData.Market).Count > 0) {
averageProfitPercent = Model.PTData.SellLog.FindAll(m => m.Market == Model.DCALogData.Market).Average(p => p.ProfitPercent); averageProfitPercent = Model.PTData.SellLog.FindAll(m => m.Market == Model.DCALogData.Market).Average(p => p.ProfitPercent);
} } *@
double investedFiatValue = Math.Round(Model.DCALogData.TotalCost * Model.Summary.MainMarketPrice, 2); double investedFiatValue = Math.Round(Model.DCALogData.TotalCost * Model.Summary.MainMarketPrice, 2);
double currentValue = Math.Round(Model.DCALogData.Amount * Model.DCALogData.CurrentPrice, 8); double currentValue = Math.Round(Model.DCALogData.Amount * Model.DCALogData.CurrentPrice, 8);

View File

@ -11,8 +11,8 @@
<div class="row"> <div class="row">
<div class="col-md-5 px-1"> <div class="col-md-5 px-1">
<div class="card-box px-2" style="height:305px;"> <div class="card-box px-2" style="height:340px;">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div> <h4 class="m-t-0 m-b-20 header-title" style="display: inline;"><b>Market Trend History </b><i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="@Math.Round(Model.DataHours, 1) hours of data available. Currently set to show @Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours hours at @Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes intervals, in general settings."></i></h4>
@if (!Model.TrendChartDataJSON.Equals("")) { @if (!Model.TrendChartDataJSON.Equals("")) {
<div class="trend-chart"> <div class="trend-chart">
<svg style="height: 300px;width: 100%;"></svg> <svg style="height: 300px;width: 100%;"></svg>
@ -22,9 +22,11 @@
} }
</div> </div>
</div> </div>
<div class="col-md-3 px-1"> <div class="col-md-3 px-1">
<div class="card-box px-3" style="height:305px;"> <div class="card-box px-3" style="height: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) {
@ -34,8 +36,8 @@
<div id="AssetDistribution" class="container"> <div id="AssetDistribution" class="container">
<div class="text-center"> <div class="text-center">
<small> <small>
<span data-toggle="tooltip" data-placement="top" title="Starting balance from PTM settings">Start: &nbsp; <text class="text-autocolor"> @Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance @Model.Summary.MainMarket </text></span> <span data-toggle="tooltip" data-placement="top" title="Starting balance from PTM settings">Start: &nbsp; <text class="text-autocolor"> @Model.MiscData.StartBalance @Model.Summary.MainMarket </text></span>
<span data-toggle="tooltip" data-placement="top" title="TCV gain on starting balance"> &emsp; &emsp; Gain:&nbsp;<text class="text-autocolor">@Math.Round(((Model.totalCurrentValue - Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance) / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance) * 100, 2)%</text></span> <span data-toggle="tooltip" data-placement="top" title="TCV gain on starting balance"> &emsp; &emsp; Gain:&nbsp;<text class="text-autocolor">@Math.Round(((Model.totalCurrentValue - Model.MiscData.StartBalance) / Model.MiscData.StartBalance) * 100, 2)%</text></span>
</small> </small>
</div> </div>
<div class="text-center"> <div class="text-center">
@ -49,8 +51,9 @@
</div> </div>
<div class="col-md-4 px-1"> <div class="col-md-4 px-1">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div> @*<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>*@
<div class="card-box px-2" style="height:305px;"> <div class="card-box px-2" style="height:340px;">
<h4 class="m-t-0 m-b-20 header-title" style="display: inline;">Daily Profit <i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="@Model.ProfitDays days of data available. Currently Set to @Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays days in general settings."></i>
@if (!Model.ProfitChartDataJSON.Equals("")) { @if (!Model.ProfitChartDataJSON.Equals("")) {
<div class="profit-chart"> <div class="profit-chart">
<svg style="height:300px;width:100%"></svg> <svg style="height:300px;width:100%"></svg>
@ -61,27 +64,31 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6 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 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" style="font-size small" 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: x-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&nbsp;&nbsp;<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>
<tbody> <tbody>
@foreach (var marketTrend in Model.MarketTrends.OrderBy(mt => mt.TrendMinutes)) { @foreach (var marketTrend in Model.MarketTrends.OrderBy(mt => mt.TrendMinutes)) {
if (Model.Summary.MarketTrendChanges.ContainsKey(marketTrend.Name)) { if (Model.Summary.MarketTrendChanges.ContainsKey(marketTrend.Name)) {
double trendChange = Model.Summary.MarketTrendChanges[marketTrend.Name].OrderByDescending(mtc => mtc.TrendDateTime).First().TrendChange; double trendChange = Model.Summary.MarketTrendChanges[marketTrend.Name].Last().TrendChange;
string trendChangeOutput = trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); string trendChangeOutput = trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
int marketCount = marketTrend.MaxMarkets; int marketCount = marketTrend.MaxMarkets;
@ -92,8 +99,12 @@
} else if (marketCount > Model.Summary.MarketSummary.Keys.Count && marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) { } else if (marketCount > Model.Summary.MarketSummary.Keys.Count && marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) {
marketCountString = Model.Summary.MarketSummary.Keys.Count.ToString(); marketCountString = Model.Summary.MarketSummary.Keys.Count.ToString();
} }
// Cache the result of SplitCamelCase(marketTrend.Name)
string splitCamelCaseName = Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name);
<tr> <tr>
<td>@Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name)</td> <td>@splitCamelCaseName</td> <!-- Use the cached value here -->
<td class="text-right">@marketCountString</td> <td class="text-right">@marketCountString</td>
<td class="text-right">@Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false)</td> <td class="text-right">@Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false)</td>
@if (marketTrend.TrendThreshold == 0) @if (marketTrend.TrendThreshold == 0)
@ -102,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>
} }
} }
@ -113,92 +124,153 @@
</div> </div>
</div> </div>
<div class="col-md-6 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" style="font-size x-small" 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 totalProfit = 0; double avgGrowthThisMonth = Model.PTData.MonthlyStats.FirstOrDefault(data => data.Order == 1)?.AvgGrowth ?? 0.0;
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit); double avgGrowthLastMonth = Model.PTData.MonthlyStats.FirstOrDefault(data => data.Order == 2)?.AvgGrowth ?? 0.0;
double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2);
double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2);
string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
if (Model.PTData.TransactionData.Transactions.Count > 0)
{
percentGainText = "<i class=\"fa fa-info-circle text-muted\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"You have added at least one manual transaction, so the total gain percentage cannot be calculated.\"></i>";
}
double todaysProfit = 0; //var startingBalance = Model.MiscData.StartBalance;
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit); var totalCurrentValue = Model.totalCurrentValue;
double todaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime); var overviewStats = Model.StatsData;
double todaysProfitFiat = Math.Round(todaysProfit * Model.Summary.MainMarketPrice, 2);
double todaysPercentGain = Math.Round(todaysProfit / todaysStartBalance * 100, 2);
double yesterdaysProfit = 0; var todaysSales = overviewStats.SalesToday;
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit); var todaysProfit = overviewStats.ProfitToday;
double yesterdaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-1)); var todaysFunding = overviewStats.FundingToday;
double yesterdaysProfitFiat = Math.Round(yesterdaysProfit * Model.Summary.MainMarketPrice, 2); var todaysPercentGain = overviewStats.ProfitPercToday + Model.PTData.Stats.TotalFundingPercToday;
double yesterdaysPercentGain = Math.Round(yesterdaysProfit / yesterdaysStartBalance * 100, 2);
double last7DaysProfit = 0; var yesterdaysSales = overviewStats.SalesYesterday;
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit); var yesterdaysProfit = overviewStats.ProfitYesterday;
double last7DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-7)); var yesterdaysFunding = overviewStats.FundingYesterday;
double last7DaysProfitFiat = Math.Round(last7DaysProfit * Model.Summary.MainMarketPrice, 2); var yesterdaysPercentGain = overviewStats.ProfitPercYesterday + Model.PTData.Stats.TotalFundingPercYesterday;
double last7DaysPercentGain = Math.Round(last7DaysProfit / last7DaysStartBalance * 100, 2);
var last7DaysSales = overviewStats.SalesWeek;
var last7DaysProfit = overviewStats.ProfitWeek;
var last7DaysFunding = overviewStats.FundingWeek;
var last7DaysPercentGain = overviewStats.ProfitPercWeek + Model.PTData.Stats.TotalFundingPercWeek;
var thisMonthSales = overviewStats.SalesThisMonth;
var thisMonthProfit = overviewStats.ProfitThisMonth;
var thisMonthFunding = overviewStats.FundingThisMonth;
var thisMonthPercentGain = avgGrowthThisMonth;
var lastMonthSales = overviewStats.SalesLastMonth;
var lastMonthProfit = overviewStats.ProfitLastMonth;
var lastMonthFunding = overviewStats.FundingLastMonth;
var lastMonthPercentGain = avgGrowthLastMonth;
var totalSales = overviewStats.TotalSales;
var totalProfit = overviewStats.TotalProfit;
var totalFunding = overviewStats.FundingTotal;
var totalPercentGain = overviewStats.TotalProfitPerc + Model.PTData.Stats.TotalFundingPerc;
double todaysProfitFiat = Math.Round((todaysProfit + todaysFunding) * Model.PTData.Misc.FiatConversionRate, 2);
double yesterdaysProfitFiat = Math.Round((yesterdaysProfit + yesterdaysFunding) * Model.PTData.Misc.FiatConversionRate, 2);
double last7DaysProfitFiat = Math.Round((last7DaysProfit + last7DaysFunding) * Model.PTData.Misc.FiatConversionRate, 2);
double thisMonthProfitFiat = Math.Round((thisMonthProfit + thisMonthFunding) * Model.PTData.Misc.FiatConversionRate, 2);
double lastMonthProfitFiat = Math.Round((lastMonthProfit + lastMonthFunding) * Model.PTData.Misc.FiatConversionRate, 2);
double totalProfitFiat = Math.Round((totalProfit + totalFunding) * Model.PTData.Misc.FiatConversionRate, 2);
bool futuresFunding = Model.PropertiesData.IsLeverageExchange;
double last30DaysProfit = 0;
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit);
double last30DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-30));
double last30DaysProfitFiat = Math.Round(last30DaysProfit * Model.Summary.MainMarketPrice, 2);
double last30DaysPercentGain = Math.Round(last30DaysProfit / last30DaysStartBalance * 100, 2);
} }
<table class="table table-sm"> <table class="table table-sm">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th class="text-right">Sales</th> <th class="text-right">Sales</th>
<th class="text-right">Profit @Model.Summary.MainMarket</th> <th class="text-right">Profit @Model.PTData.Misc.Market</th>
<th class="text-right">Profit @Model.Summary.MainFiatCurrency</th> @if (futuresFunding)
{
<th class="text-right">Funding</th>
}
<th class="text-right">@Model.PTData.Properties.Currency</th>
<th class="text-right">Gain</th> <th class="text-right">Gain</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<th>Today</th> <th>Today</th>
<td class="text-right">@Model.PTData.SellLogToday.Count</td> <td class="text-right">@overviewStats.SalesToday</td>
<td class="text-right text-autocolor">@todaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor">@todaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + todaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> @if (futuresFunding)
<td class="text-right text-autocolor">@todaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> {
<td class="text-right text-autocolor">@todaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
}
<td class="text-right text-autocolor">@Html.Raw(todaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@todaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr> </tr>
<tr> <tr>
<th>Yesterday</th> <th>Yesterday</th>
<td class="text-right">@Model.PTData.SellLogYesterday.Count</td> <td class="text-right">@yesterdaysSales</td>
<td class="text-right text-autocolor">@yesterdaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> <td class="text-right text-autocolor">@yesterdaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + yesterdaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> @if (futuresFunding)
<td class="text-right text-autocolor">@yesterdaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> {
<td class="text-right text-autocolor">@yesterdaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
}
<td class="text-right text-autocolor">@Html.Raw(yesterdaysProfitFiat.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">@Model.PTData.SellLogLast7Days.Count</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>
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + last7DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> @if (futuresFunding)
<td class="text-right text-autocolor">@last7DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> {
<td class="text-right text-autocolor">@last7DaysFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
}
<td class="text-right text-autocolor">@Html.Raw(last7DaysProfitFiat.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>Last 30 Days</th> @{
<td class="text-right">@Model.PTData.SellLogLast30Days.Count</td> var timeParts = @Model.MiscData.TimeZoneOffset.Split(':');
<td class="text-right text-autocolor">@last30DaysProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td> var timeZoneOffsetHours = int.Parse(timeParts[0]);
<td class="text-right text-autocolor">@Html.Raw(Model.MainFiatCurrencySymbol + last30DaysProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> var timeZoneOffset = TimeSpan.FromHours(timeZoneOffsetHours);
<td class="text-right text-autocolor">@last30DaysPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> 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 text-autocolor">@thisMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
@if (futuresFunding)
{
<td class="text-right text-autocolor">@thisMonthFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
}
<td class="text-right text-autocolor">@Html.Raw(thisMonthProfitFiat.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>
@{
var previousMonthDateTime = currentDateTime.AddMonths(-1);
var previousMonthName = previousMonthDateTime.ToString("MMMM");
}
<th>@previousMonthName</th>
<td class="text-right">@lastMonthSales</td>
<td class="text-right text-autocolor">@lastMonthProfit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
@if (futuresFunding)
{
<td class="text-right text-autocolor">@lastMonthFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
}
<td class="text-right text-autocolor">@Html.Raw(lastMonthProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@lastMonthPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr> </tr>
<tr> <tr>
<th>Total</th> <th>Total</th>
<td class="text-right">@Model.PTData.SellLog.Count</td> <td class="text-right">@totalSales</td>
<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">@Html.Raw(Model.MainFiatCurrencySymbol + totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td> @if (futuresFunding)
<td class="text-right text-autocolor">@Html.Raw(percentGainText)</td> {
<td class="text-right text-autocolor">@totalFunding.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
}
<td class="text-right text-autocolor">@Html.Raw(totalProfitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right text-autocolor">@totalPercentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -210,74 +282,176 @@
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/nvd3/nv.d3.min.js"></script> <script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/nvd3/nv.d3.min.js"></script>
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw.js"></script> <script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw.js"></script>
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw-init.js"></script> <script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw-init.js"></script>
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function () { $(document).ready(function () {
$(".cdev").circlos(); $(".cdev").circlos();
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false); $('.text-autocolor').autocolor(false);
var assetDistributionChart; // Keep a reference to the chart
var assetDistributionData; // Keep a reference to the data
@if (!Model.AssetDistributionData.Equals("")) { @if (!Model.AssetDistributionData.Equals("")) {
<text> <text>
nv.addGraph(function() { nv.addGraph(function() {
var chart = nv.models.pieChart() assetDistributionChart = nv.models.pieChart()
.x(function(d) { return d.label }) .x(function(d) { return d.label })
.y(function(d) { return d.value }) .y(function(d) { return d.value })
.showLabels(true) //Display pie labels .showLabels(true)
.labelThreshold(.1) //Configure the minimum slice size for labels to show up .labelThreshold(.1)
.labelType("percent") //Configure what type of data to show in the label. Can be "key", "value" or "percent" .labelType("percent")
.donut(true) //Turn on Donut mode. Makes pie chart look tasty! .donut(true)
.donutRatio(0.3) //Configure how big you want the donut hole size to be. .donutRatio(0.3);
;
assetDistributionData = @Html.Raw(Model.AssetDistributionData);
d3.select("#AssetDistribution svg") d3.select("#AssetDistribution svg")
.datum(@Html.Raw(Model.AssetDistributionData)) .datum(assetDistributionData)
.transition().duration(350) .transition().duration(0)
.call(chart); .call(assetDistributionChart);
return chart;
// Add mouseleave, and mousemove event listeners to hide tooltip
d3.select('.profit-chart').on('mouseleave', function() {
d3.select('.nvtooltip').style('opacity', 0);
});
d3.select('body').on('mousemove', function() {
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
var mouseX = d3.event.clientX;
var mouseY = d3.event.clientY;
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
d3.select('.nvtooltip').style('opacity', 0);
}
});
nv.utils.windowResize(assetDistributionChart.update);
return assetDistributionChart;
}); });
</text> </text>
} }
}); });
</script>
<script type="text/javascript">
(function ($) { (function ($) {
'use strict'; 'use strict';
$('[role="tooltip"]').remove(); $('[role="tooltip"]').remove();
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false); $('.text-autocolor').autocolor(false);
@if (!Model.Summary.CurrentGlobalSetting.SettingName.Equals(Model.LastGlobalSetting)) { var trendChart; // Keep a reference to the chart
<text> var trendData; // Keep a reference to the data
$.Notification.notify('success', 'top left', '@Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName) now active!', 'PTMagic switched Profit Trailer settings to "@Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName)".');
</text>
}
@if (!Model.TrendChartDataJSON.Equals("")) { @if (!Model.TrendChartDataJSON.Equals("")) {
<text> <text>
nv.addGraph(function () { nv.addGraph(function () {
var lineChart = nv.models.lineChart(); trendChart = nv.models.lineChart();
var height = 300; var height = 300;
var chartData = @Html.Raw(Model.TrendChartDataJSON); trendChart.useInteractiveGuideline(true);
lineChart.useInteractiveGuideline(true); trendChart.xAxis.tickFormat(function (d) { return d3.time.format('%H:%M')(new Date(d)); });
lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%H:%M')(new Date(d)); }); trendChart.yAxis.axisLabel('Trend %').tickFormat(d3.format(',.2f'));
lineChart.yAxis.axisLabel('Trend %').tickFormat(d3.format(',.2f'));
d3.select('.trend-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart); trendData = @Html.Raw(Model.TrendChartDataJSON);
//nv.utils.windowResize(lineChart.update); v1.3.0 => Removed this line to prevent memory leak
return lineChart; d3.select('.trend-chart svg')
.datum(trendData)
.transition().duration(0)
.call(trendChart);
// Add mouseleave, and mousemove event listeners to hide tooltip
d3.select('.profit-chart').on('mouseleave', function() {
d3.select('.nvtooltip').style('opacity', 0);
}); });
</text>
d3.select('body').on('mousemove', function() {
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
var mouseX = d3.event.clientX;
var mouseY = d3.event.clientY;
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
d3.select('.nvtooltip').style('opacity', 0);
} }
@if (!Model.ProfitChartDataJSON.Equals("")) { });
<text> nv.utils.windowResize(trendChart.update);
nv.addGraph(function () { return trendChart;
var lineChart = nv.models.lineChart();
var height = 300;
var chartData = @Html.Raw(Model.ProfitChartDataJSON);
lineChart.useInteractiveGuideline(true);
lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%Y/%m/%d')(new Date(d)); });
lineChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f'));
d3.select('.profit-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
//nv.utils.windowResize(lineChart.update); v1.3.0 => Removed this line to prevent memory leak
return lineChart;
}); });
</text> </text>
} }
})(jQuery); })(jQuery);
</script> </script>
<script type="text/javascript">
(function ($) {
'use strict';
$('[role="tooltip"]').remove();
$('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false);
var profitChart; // Keep a reference to the chart
var profitData; // Keep a reference to the data
@if (!Model.ProfitChartDataJSON.Equals("")) {
<text>
nv.addGraph(function () {
profitChart = nv.models.lineChart();
var height = 300;
profitChart.useInteractiveGuideline(true);
profitChart.xAxis.tickFormat(function (d) { return d3.time.format('%m/%d')(new Date(d)); });
profitChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f'));
profitData = @Html.Raw(Model.ProfitChartDataJSON);
d3.select('.profit-chart svg')
.datum(profitData)
.transition().duration(0)
.call(profitChart);
// Add mouseleave, and mousemove event listeners to hide tooltip
d3.select('.profit-chart').on('mouseleave', function() {
d3.select('.nvtooltip').style('opacity', 0);
});
d3.select('body').on('mousemove', function() {
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
var mouseX = d3.event.clientX;
var mouseY = d3.event.clientY;
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
d3.select('.nvtooltip').style('opacity', 0);
}
});
nv.utils.windowResize(profitChart.update);
return profitChart;
});
</text>
}
})(jQuery);
</script>
<script type="text/javascript">
$(document).ready(function(){
var originalLeave = $.fn.tooltip.Constructor.prototype.leave;
$.fn.tooltip.Constructor.prototype.leave = function(obj){
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
var container, timeout;
originalLeave.call(this, obj);
if(obj.currentTarget) {
container = $(obj.currentTarget).siblings('.tooltip');
timeout = self.timeout;
container.one('mouseenter', function(){
//We entered the actual tooltip call off the dogs
clearTimeout(timeout);
//Let's monitor tooltip content instead
container.one('mouseleave', function(){
$.fn.tooltip.Constructor.prototype.leave.call(self, self);
});
});
}
};
$('[data-toggle="tooltip"]').tooltip();
});
</script>

View File

@ -6,14 +6,20 @@ using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects; using Core.Main.DataObjects;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
using Core.MarketAnalyzer; using System.Globalization;
using System.Text;
namespace Monitor.Pages namespace Monitor.Pages
{ {
public class DashboardBottomModel : _Internal.BasePageModelSecureAJAX public class DashboardBottomModel : _Internal.BasePageModelSecureAJAX
{ {
public ProfitTrailerData PTData = null; public ProfitTrailerData PTData = null;
public StatsData StatsData { get; set; }
public PropertiesData PropertiesData { get; set; }
public MiscData MiscData { get; set; }
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>(); public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
public double DataHours { get; set; }
public int ProfitDays { get; set; }
public string TrendChartDataJSON = ""; public string TrendChartDataJSON = "";
public string ProfitChartDataJSON = ""; public string ProfitChartDataJSON = "";
public string LastGlobalSetting = "Default"; public string LastGlobalSetting = "Default";
@ -26,12 +32,18 @@ namespace Monitor.Pages
base.Init(); base.Init();
BindData(); BindData();
BuildAssetDistributionData();
} }
private void BindData() private void BindData()
{ {
PTData = this.PtDataObject; PTData = this.PtDataObject;
StatsData = this.PTData.Stats;
PropertiesData = this.PTData.Properties;
MiscData = this.PTData.Misc;
List<MonthlyStatsData> monthlyStatsData = this.PTData.MonthlyStats;
List<DailyPNLData> dailyPNLData = this.PTData.DailyPNL;
// Cleanup temp files // Cleanup temp files
FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5); FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5);
@ -51,27 +63,24 @@ namespace Monitor.Pages
MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList(); MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList();
BuildMarketTrendChartData(); BuildMarketTrendChartData();
BuildAssetDistributionData();
BuildProfitChartData(); BuildProfitChartData();
} }
private void BuildMarketTrendChartData() private void BuildMarketTrendChartData()
{ {
List<string> trendChartData = new List<string>();
if (MarketTrends.Count > 0) if (MarketTrends.Count > 0)
{ {
TrendChartDataJSON = "[";
int mtIndex = 0; int mtIndex = 0;
foreach (MarketTrend mt in MarketTrends) foreach (MarketTrend mt in MarketTrends)
{ {
if (mt.DisplayGraph) if (mt.DisplayGraph)
{ {
string lineColor = ""; string lineColor = mtIndex < Constants.ChartLineColors.Length
if (mtIndex < Constants.ChartLineColors.Length) ? Constants.ChartLineColors[mtIndex]
{ : Constants.ChartLineColors[mtIndex - 20];
lineColor = Constants.ChartLineColors[mtIndex];
}
else
{
lineColor = Constants.ChartLineColors[mtIndex - 20];
}
if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) if (Summary.MarketTrendChanges.ContainsKey(mt.Name))
{ {
@ -79,85 +88,141 @@ namespace Monitor.Pages
if (marketTrendChangeSummaries.Count > 0) if (marketTrendChangeSummaries.Count > 0)
{ {
if (!TrendChartDataJSON.Equals("[")) TrendChartDataJSON += ","; List<string> trendValues = new List<string>();
TrendChartDataJSON += "{"; // Sort marketTrendChangeSummaries by TrendDateTime
TrendChartDataJSON += "key: '" + SystemHelper.SplitCamelCase(mt.Name) + "',"; marketTrendChangeSummaries = marketTrendChangeSummaries.OrderBy(m => m.TrendDateTime).ToList();
TrendChartDataJSON += "color: '" + lineColor + "',";
TrendChartDataJSON += "values: [";
// Get trend ticks for chart // Get trend ticks for chart
DateTime currentDateTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0); TimeSpan offset;
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
{
offset = TimeSpan.Zero; // If offset is invalid, set it to zero
}
DateTime currentDateTime = DateTime.UtcNow;
DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
DateTime endDateTime = currentDateTime; DateTime endDateTime = currentDateTime;
int trendChartTicks = 0;
// Ensure startDateTime doesn't exceed the available data
DateTime earliestTrendDateTime = marketTrendChangeSummaries.Min(mtc => mtc.TrendDateTime);
startDateTime = startDateTime > earliestTrendDateTime ? startDateTime : earliestTrendDateTime;
DataHours = (currentDateTime - earliestTrendDateTime).TotalHours;
// Cache the result of SplitCamelCase(mt.Name)
string splitCamelCaseName = SystemHelper.SplitCamelCase(mt.Name);
for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes))
{ {
List<MarketTrendChange> tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); // Use binary search to find the range of items that match the condition
if (tickRange.Count > 0) 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 = tickRange.First(); MarketTrendChange mtc = marketTrendChangeSummaries[index];
if (tickTime != startDateTime) TrendChartDataJSON += ",\n";
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0;
TrendChartDataJSON += "{ x: new Date('" + tickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; // Adjust tickTime to the desired timezone before converting to string
trendChartTicks++; DateTime adjustedTickTime = tickTime.Add(isNegative ? -offset : offset);
trendValues.Add("{ x: new Date('" + adjustedTickTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
} }
} }
// Add most recent tick // Add most recent tick
List<MarketTrendChange> latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); MarketTrendChange latestMtc = marketTrendChangeSummaries.Last();
if (latestTickRange.Count > 0) if (Double.IsInfinity(latestMtc.TrendChange)) latestMtc.TrendChange = 0;
{
MarketTrendChange mtc = latestTickRange.First(); // Adjust latestMtc.TrendDateTime to the desired timezone before converting to string
if (trendChartTicks > 0) TrendChartDataJSON += ",\n"; DateTime adjustedLatestTrendDateTime = latestMtc.TrendDateTime.Add(isNegative ? -offset : offset);
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; trendValues.Add("{ x: new Date('" + adjustedLatestTrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + latestMtc.TrendChange.ToString("0.00", CultureInfo.InvariantCulture) + "}");
TrendChartDataJSON += "{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
} // Use cached splitCamelCaseName
TrendChartDataJSON += "]"; trendChartData.Add("{ key: '" + splitCamelCaseName + "', color: '" + lineColor + "', values: [" + string.Join(",\n", trendValues) + "] }");
TrendChartDataJSON += "}";
mtIndex++; mtIndex++;
} }
} }
} }
} }
TrendChartDataJSON += "]";
} }
TrendChartDataJSON = "[" + string.Join(",", trendChartData) + "]";
} }
private void BuildProfitChartData() private void BuildProfitChartData()
{ {
int tradeDayIndex = 0; List<object> profitPerDayList = new List<object>();
string profitPerDayJSON = "";
if (PTData.SellLog.Count > 0) if (PTData.DailyPNL.Count > 0)
{ {
DateTime minSellLogDate = PTData.SellLog.OrderBy(sl => sl.SoldDate).First().SoldDate.Date; // Get timezone offset
DateTime graphStartDate = DateTime.UtcNow.Date.AddDays(-30); TimeSpan offset;
if (minSellLogDate > graphStartDate) bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
{ {
graphStartDate = minSellLogDate; offset = TimeSpan.Zero; // If offset is invalid, set it to zero
} }
for (DateTime salesDate = graphStartDate; salesDate <= DateTime.UtcNow.Date; salesDate = salesDate.AddDays(1))
DateTime endDate = DateTime.UtcNow.Add(isNegative ? -offset : offset).Date;
// Parse dates once and adjust them to the local timezone
Dictionary<DateTime, DailyPNLData> dailyPNLByDate = PTData.DailyPNL
.Select(data => {
DateTime dateUtc = DateTime.ParseExact(data.Date, "d-M-yyyy", CultureInfo.InvariantCulture);
DateTime dateLocal = dateUtc.Add(isNegative ? -offset : offset);
return new { Date = dateLocal.Date, Data = data };
})
.ToDictionary(
item => item.Date,
item => item.Data
);
DateTime earliestDataDate = dailyPNLByDate.Keys.Min();
DateTime startDate = endDate.AddDays(-PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays - 1); // Fetch data for timeframe + 1 days
if (startDate < earliestDataDate)
{ {
if (tradeDayIndex > 0) startDate = earliestDataDate;
}
// Calculate the total days of data available
ProfitDays = (endDate - startDate).Days;
double previousDayCumulativeProfit = 0;
bool isFirstDay = true;
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{ {
profitPerDayJSON += ",\n"; // Use the dictionary to find the DailyPNLData for the date
if (dailyPNLByDate.TryGetValue(date, out DailyPNLData dailyPNL))
{
if (isFirstDay)
{
isFirstDay = false;
} }
int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Count; else
double profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Sum(t => t.Profit); {
double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2); // Calculate the profit for the current day
profitPerDayJSON += "{x: new Date('" + salesDate.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}"; double profitFiat = Math.Round(dailyPNL.CumulativeProfitCurrency - previousDayCumulativeProfit, 2);
tradeDayIndex++;
// Add the data point to the list
profitPerDayList.Add(new { x = new DateTimeOffset(date).ToUnixTimeMilliseconds(), y = profitFiat });
} }
ProfitChartDataJSON = "["; previousDayCumulativeProfit = dailyPNL.CumulativeProfitCurrency;
ProfitChartDataJSON += "{";
ProfitChartDataJSON += "key: 'Profit in " + Summary.MainFiatCurrency + "',";
ProfitChartDataJSON += "color: '" + Constants.ChartLineColors[1] + "',";
ProfitChartDataJSON += "values: [" + profitPerDayJSON + "]";
ProfitChartDataJSON += "}";
ProfitChartDataJSON += "]";
} }
} }
// Convert the list to a JSON string using Newtonsoft.Json
ProfitChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] {
new {
key = "Profit in " + PTData.Misc.Market,
color = Constants.ChartLineColors[1],
values = profitPerDayList
}
});
}
}
private void BuildAssetDistributionData() private void BuildAssetDistributionData()
{ {
// the per PT-Eelroy, the PT API doesn't provide these values when using leverage, so they are calculated here to cover either case. // the per PT-Eelroy, the PT API doesn't provide these values when using leverage, so they are calculated here to cover either case.
@ -171,7 +236,6 @@ namespace Monitor.Pages
foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog) foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog)
{ {
string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive);
// Aggregate totals // Aggregate totals
double leverage = dcaLogEntry.Leverage; double leverage = dcaLogEntry.Leverage;
if (leverage == 0) if (leverage == 0)

View File

@ -1,86 +0,0 @@
@page
@model SalesListModel
@{
Layout = null;
}
<div class="modal-header">
<h4 class="modal-title mt-0">Showing @Model.SellLog.Count sales for @Model.SalesTimeframe</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<table id="sales-list" class="table table-sm">
<thead>
<tr>
<th>Sold Time</th>
<th>Market</th>
<th class="text-right">Avg. Bought Price</th>
<th class="text-right">DCA</th>
<th class="text-right">Sold Price</th>
<th class="text-right">Sold Amount</th>
<th class="text-right">Bought Cost</th>
<th class="text-right">Sold Value</th>
<th class="text-right">Profit @Model.Summary.MainMarket</th>
<th class="text-right">Profit @Model.Summary.MainFiatCurrency</th>
<th class="text-right">Profit %</th>
</tr>
</thead>
<tbody>
@foreach (Core.Main.DataObjects.PTMagicData.SellLogData sellLogEntry in Model.SellLog) {
double profitFiat = Math.Round(sellLogEntry.Profit * Model.Summary.MainMarketPrice, 2);
<tr>
<td>@sellLogEntry.SoldDate.ToShortDateString() @sellLogEntry.SoldDate.ToLongTimeString()</td>
<td><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, sellLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@sellLogEntry.Market</a></td>
<td class="text-right">@sellLogEntry.AverageBuyPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right">
@if (sellLogEntry.BoughtTimes > 0) {
@sellLogEntry.BoughtTimes;
}
</td>
<td class="text-right">@sellLogEntry.SoldPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right">@sellLogEntry.SoldAmount.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right">@sellLogEntry.TotalCost.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right">@sellLogEntry.SoldValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@sellLogEntry.Profit.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@profitFiat.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right text-autocolor">@sellLogEntry.ProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
</tr>
}
</tbody>
</table>
</div>
<div class="modal-footer">
</div>
<script type="text/javascript">
$(function () {
//Buttons examples
var table = $('#sales-list').DataTable({
lengthChange: false,
searching: false,
paging: false,
info: false,
ordering: false,
buttons: ['copy', 'excel', 'pdf']
});
table.buttons().container()
.appendTo('#sales-list_wrapper .col-md-6:eq(0)');
$('.btn-trend-relation').click(function () {
var relation = $(this).data('trend-relation');
$('.btn-trend-relation').addClass('btn-custom');
$(this).removeClass('btn-custom');
if (relation == 'absolute') {
$('#trends-absolute').removeClass('hidden');
$('#trends-relative').addClass('hidden');
} else {
$('#trends-absolute').addClass('hidden');
$('#trends-relative').removeClass('hidden');
}
});
})
</script>

View File

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects;
using Core.Main.DataObjects.PTMagicData;
using Core.MarketAnalyzer;
namespace Monitor.Pages {
public class SalesListModel : _Internal.BasePageModelSecure {
public ProfitTrailerData PTData = null;
private string salesDateString = "";
private string salesMonthString = "";
public string SalesTimeframe = "";
public DateTime SalesDate = Constants.confMinDate;
public List<SellLogData> SellLog = new List<SellLogData>();
public void OnGet() {
// Initialize Config
base.Init();
BindData();
}
private void BindData() {
salesDateString = GetStringParameter("d", "");
salesMonthString = GetStringParameter("m", "");
PTData = this.PtDataObject;
if (!salesDateString.Equals("")) {
SalesDate = SystemHelper.TextToDateTime(salesDateString, Constants.confMinDate);
if (SalesDate != Constants.confMinDate) {
SalesTimeframe = SalesDate.ToShortDateString();
SellLog = PTData.SellLog.FindAll(sl => sl.SoldDate.Date == SalesDate.Date).OrderByDescending(sl => sl.SoldDate).ToList();
}
} else if (!salesMonthString.Equals("")) {
SalesDate = SystemHelper.TextToDateTime(salesMonthString + "-01", Constants.confMinDate);
if (SalesDate != Constants.confMinDate) {
SalesTimeframe = SalesDate.ToString("MMMM yyyy", new System.Globalization.CultureInfo("en-US"));
SellLog = PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == SalesDate.Month && sl.SoldDate.Date.Year == SalesDate.Year).OrderByDescending(sl => sl.SoldDate).ToList();
}
}
}
}
}

View File

@ -50,7 +50,7 @@ namespace Monitor
services.AddDistributedMemoryCache(); services.AddDistributedMemoryCache();
services.AddSession(options => services.AddSession(options =>
{ {
options.IdleTimeout = TimeSpan.FromSeconds(900); options.IdleTimeout = TimeSpan.FromSeconds(1800);
options.Cookie.HttpOnly = true; options.Cookie.HttpOnly = true;
options.Cookie.Name = "PTMagicMonitor" + systemConfiguration.GeneralSettings.Monitor.Port.ToString(); options.Cookie.Name = "PTMagicMonitor" + systemConfiguration.GeneralSettings.Monitor.Port.ToString();
}); });

View File

@ -639,7 +639,7 @@ a.text-dark:hover {
} }
.bg-success { .bg-success {
background-color: #81c868 !important; background-color: #296a12 !important;
} }
.bg-info { .bg-info {
@ -647,11 +647,11 @@ a.text-dark:hover {
} }
.bg-warning { .bg-warning {
background-color: #ffbd4a !important; background-color: #563a09 !important;
} }
.bg-danger { .bg-danger {
background-color: #f05050 !important; background-color: #6e0e0e !important;
} }
.bg-muted { .bg-muted {
@ -759,7 +759,7 @@ a.text-dark:hover {
} }
.label-success { .label-success {
background-color: #81c868; background-color: #3b5e2f;
} }
.label-info { .label-info {
@ -767,11 +767,11 @@ a.text-dark:hover {
} }
.label-warning { .label-warning {
background-color: #ffbd4a; background-color: #847f0a;
} }
.label-danger { .label-danger {
background-color: #f05050; background-color: #601f1f;
} }
.label-purple { .label-purple {
@ -829,7 +829,7 @@ a.text-dark:hover {
} }
.badge-success { .badge-success {
background-color: #81c868; background-color: #307516;
} }
.badge-info { .badge-info {
@ -837,11 +837,11 @@ a.text-dark:hover {
} }
.badge-warning { .badge-warning {
background-color: #ffbd4a; background-color: #6f5019;
} }
.badge-danger { .badge-danger {
background-color: #f05050; background-color: #6a1414;
} }
.badge-purple { .badge-purple {

37
PTMagic.sln Normal file
View File

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{DC985FA9-87CC-4C60-A587-E646E74A1A4A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monitor", "Monitor\Monitor.csproj", "{D967C466-E4BF-40A9-84FD-698079FAD3D5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PTMagic", "PTMagic\PTMagic.csproj", "{D81F5541-438E-42C7-B6E7-B859875B18A8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC985FA9-87CC-4C60-A587-E646E74A1A4A}.Release|Any CPU.Build.0 = Release|Any CPU
{D967C466-E4BF-40A9-84FD-698079FAD3D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D967C466-E4BF-40A9-84FD-698079FAD3D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D967C466-E4BF-40A9-84FD-698079FAD3D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D967C466-E4BF-40A9-84FD-698079FAD3D5}.Release|Any CPU.Build.0 = Release|Any CPU
{D81F5541-438E-42C7-B6E7-B859875B18A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D81F5541-438E-42C7-B6E7-B859875B18A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D81F5541-438E-42C7-B6E7-B859875B18A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D81F5541-438E-42C7-B6E7-B859875B18A8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3646F7E-91F6-4D66-9B1B-2B74317580A4}
EndGlobalSection
EndGlobal

View File

@ -6,7 +6,7 @@ using Core.Helper;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
[assembly: AssemblyVersion("2.6.1")] [assembly: AssemblyVersion("2.7.1")]
[assembly: AssemblyProduct("PT Magic")] [assembly: AssemblyProduct("PT Magic")]
namespace PTMagic namespace PTMagic

View File

@ -10,13 +10,10 @@
"ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list) "ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings) "ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings)
"Exchange": "Bittrex", // The exchange your are running Profit Trailer on "Exchange": "Bittrex", // The exchange your are running Profit Trailer on
"StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer
"TimezoneOffset": "+0:00", // Your timezone offset from UTC time "TimezoneOffset": "+0:00", // Your timezone offset from UTC time
"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor
"FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute
"InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them
"CoinMarketCapAPIKey": "", //CoinMarketCap Api //"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key
"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key
}, },
"Monitor": { "Monitor": {
"IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start
@ -24,8 +21,9 @@
"Port": 8080, // The port you want to run your monitor on "Port": 8080, // The port you want to run your monitor on
"RootUrl": "/", // The root Url of your monitor "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) "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 "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 "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 "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 "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
"MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer

View File

@ -10,13 +10,10 @@
"ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list) "ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings) "ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings)
"Exchange": "Bittrex", // The exchange your are running Profit Trailer on "Exchange": "Bittrex", // The exchange your are running Profit Trailer on
"StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer
"TimezoneOffset": "+0:00", // Your timezone offset from UTC time "TimezoneOffset": "+0:00", // Your timezone offset from UTC time
"MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor
"FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute "FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute
"InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them
"CoinMarketCapAPIKey": "", //CoinMarketCap Api "CoinMarketCapAPIKey": "", //CoinMarketCap Api
"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key
}, },
"Monitor": { "Monitor": {
"IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start
@ -24,8 +21,9 @@
"Port": 8080, // The port you want to run your monitor on "Port": 8080, // The port you want to run your monitor on
"RootUrl": "/", // The root Url of your monitor "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) "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 "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 "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 "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 "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
"MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer

View File

@ -16,7 +16,7 @@
- This version of PT Magic only works with Profit Trailer v2.4.x and above - This version of PT Magic only works with Profit Trailer v2.4.x and above
### .Net ### .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 ### 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. - **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.