2018-05-22 10:11:50 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using Core.Main ;
2024-01-15 15:12:46 +01:00
using System.Globalization ;
2018-05-22 10:11:50 +02:00
using Core.Main.DataObjects ;
using Core.Main.DataObjects.PTMagicData ;
2024-01-19 00:11:16 +01:00
2018-12-15 22:07:29 +01:00
namespace Monitor.Pages
{
public class SalesAnalyzer : _Internal . BasePageModelSecure
{
2018-05-22 10:11:50 +02:00
public ProfitTrailerData PTData = null ;
2024-01-15 15:12:46 +01:00
public MiscData MiscData { get ; set ; }
public PropertiesData PropertiesData { get ; set ; }
public StatsData StatsData { get ; set ; }
public List < DailyPNLData > DailyPNL { get ; set ; }
2024-01-16 17:56:52 +01:00
public List < DailyTCVData > DailyTCV { get ; set ; }
2024-01-17 18:13:09 +01:00
public List < ProfitablePairsData > ProfitablePairs { get ; set ; }
2024-01-19 00:11:16 +01:00
public List < DailyStatsData > DailyStats { get ; set ; }
2024-01-16 17:56:52 +01:00
public int ProfitDays { get ; set ; }
public int TCVDays { get ; set ; }
2024-01-19 00:11:16 +01:00
public int SalesDays { get ; set ; }
2024-01-15 15:12:46 +01:00
public List < MonthlyStatsData > MonthlyStats { get ; set ; }
2018-05-22 10:11:50 +02:00
public string TradesChartDataJSON = "" ;
2024-01-16 17:56:52 +01:00
public string CumulativeProfitChartDataJSON = "" ;
public string TCVChartDataJSON = "" ;
2018-05-22 10:11:50 +02:00
public string ProfitChartDataJSON = "" ;
2024-01-19 00:11:16 +01:00
public string SalesChartDataJSON = "" ;
2018-05-22 10:11:50 +02:00
public IEnumerable < KeyValuePair < string , double > > TopMarkets = null ;
public DateTime MinSellLogDate = Constants . confMinDate ;
public DateTimeOffset DateTimeNow = Constants . confMinDate ;
2024-01-19 00:11:16 +01:00
2024-01-15 15:12:46 +01:00
2018-12-15 22:07:29 +01:00
public void OnGet ( )
{
2018-05-22 10:11:50 +02:00
base . Init ( ) ;
2018-12-15 22:07:29 +01:00
2018-05-22 10:11:50 +02:00
BindData ( ) ;
}
2024-01-15 15:12:46 +01:00
2018-12-15 22:07:29 +01:00
private void BindData ( )
{
2019-10-13 22:08:48 +02:00
PTData = this . PtDataObject ;
2024-01-15 15:12:46 +01:00
MiscData = this . PTData . Misc ;
PropertiesData = this . PTData . Properties ;
StatsData = this . PTData . Stats ;
MonthlyStats = this . PTData . MonthlyStats ;
DailyPNL = this . PTData . DailyPNL ;
2024-01-16 17:56:52 +01:00
DailyTCV = this . PTData . DailyTCV ;
2024-01-17 18:13:09 +01:00
ProfitablePairs = this . PTData . ProfitablePairs ;
2024-01-19 00:11:16 +01:00
DailyStats = this . PTData . DailyStats ;
2024-01-15 15:12:46 +01:00
2018-05-22 10:11:50 +02:00
// Convert local offset time to UTC
TimeSpan offsetTimeSpan = TimeSpan . Parse ( PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . Replace ( "+" , "" ) ) ;
DateTimeNow = DateTimeOffset . UtcNow . ToOffset ( offsetTimeSpan ) ;
BuildSalesChartData ( ) ;
2024-01-16 17:56:52 +01:00
BuildProfitChartData ( ) ;
BuildCumulativeProfitChartData ( ) ;
BuildTCVChartData ( ) ;
2024-01-15 15:12:46 +01:00
}
2024-01-19 00:11:16 +01:00
2024-01-16 17:56:52 +01:00
private void BuildTCVChartData ( )
{
List < object > TCVPerDayList = new List < object > ( ) ;
if ( PTData . DailyTCV . 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 , 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
}
} ) ;
}
}
2024-01-15 15:12:46 +01:00
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 ) ;
2018-05-22 10:11:50 +02:00
}
2024-01-19 00:11:16 +01:00
2018-12-15 22:07:29 +01:00
private void BuildSalesChartData ( )
{
2024-01-19 00:11:16 +01:00
List < object > salesPerDayList = new List < object > ( ) ;
List < object > buysPerDayList = new List < object > ( ) ; // New list for daily buys
if ( PTData . DailyStats . Count > 0 )
2018-12-15 22:07:29 +01:00
{
2024-01-19 00:11:16 +01:00
// 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 , DailyStatsData > salesCountByDate = PTData . DailyStats
. ToDictionary (
data = > DateTime . ParseExact ( data . Date , "d-M-yyyy" , CultureInfo . InvariantCulture ) ,
data = > data
) ;
DateTime earliestDataDate = salesCountByDate . Keys . Min ( ) ;
DateTime startDate = earliestDataDate ;
// Calculate the total days of data available
SalesDays = ( endDate - startDate ) . Days ;
int counter = 0 ;
for ( DateTime date = startDate ; date < = endDate & & counter < 30 ; date = date . AddDays ( 1 ) ) // Check the counter
{
if ( salesCountByDate . TryGetValue ( date , out DailyStatsData dailyStatsData ) )
{
// Use the totalSales value directly
salesPerDayList . Add ( new { x = new DateTimeOffset ( date ) . ToUnixTimeMilliseconds ( ) , y = dailyStatsData . TotalSales } ) ;
// Add daily buys to the list
buysPerDayList . Add ( new { x = new DateTimeOffset ( date ) . ToUnixTimeMilliseconds ( ) , y = dailyStatsData . TotalBuys } ) ;
}
}
// Convert the lists to a JSON string using Newtonsoft.Json
SalesChartDataJSON = Newtonsoft . Json . JsonConvert . SerializeObject ( new [ ] {
new {
key = "Sales" ,
color = Constants . ChartLineColors [ 1 ] ,
values = salesPerDayList
} ,
new { // New JSON object for daily buys
key = "Buys" ,
color = Constants . ChartLineColors [ 0 ] , // Use a different color for buys
values = buysPerDayList
}
} ) ;
2018-05-22 10:11:50 +02:00
}
}
2024-01-19 00:11:16 +01:00
}
2018-05-22 10:11:50 +02:00
}